mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2018.3' into bp-52012
This commit is contained in:
commit
37666bd433
11 changed files with 144 additions and 22 deletions
|
@ -86,7 +86,7 @@ Dependencies
|
|||
|
||||
Salt should run on any Unix-like platform so long as the dependencies are met.
|
||||
|
||||
* `Python 2.7`_ >= 2.7 <3.0
|
||||
* `Python`_ - Python2 >= 2.7, Python3 >= 3.4
|
||||
* `msgpack-python`_ - High-performance message interchange format
|
||||
* `YAML`_ - Python YAML bindings
|
||||
* `Jinja2`_ - parsing Salt States (configurable in the master settings)
|
||||
|
@ -95,7 +95,7 @@ Salt should run on any Unix-like platform so long as the dependencies are met.
|
|||
cloud service providers using a unified API
|
||||
* `Requests`_ - HTTP library
|
||||
* `Tornado`_ - Web framework and asynchronous networking library
|
||||
* `futures`_ - Backport of the concurrent.futures package from Python 3.2
|
||||
* `futures`_ - Python2 only dependency. Backport of the concurrent.futures package from Python 3.2
|
||||
|
||||
Depending on the chosen Salt transport, `ZeroMQ`_ or `RAET`_, dependencies
|
||||
vary:
|
||||
|
@ -142,7 +142,7 @@ Optional Dependencies
|
|||
settings)
|
||||
* gcc - dynamic `Cython`_ module compiling
|
||||
|
||||
.. _`Python 2.7`: http://python.org/download/
|
||||
.. _`Python`: http://python.org/download/
|
||||
.. _`ZeroMQ`: http://zeromq.org/
|
||||
.. _`pyzmq`: https://github.com/zeromq/pyzmq
|
||||
.. _`msgpack-python`: https://pypi.python.org/pypi/msgpack-python/
|
||||
|
|
|
@ -7,8 +7,8 @@ Salt compatibility code
|
|||
# Import python libs
|
||||
from __future__ import absolute_import, unicode_literals, print_function
|
||||
import sys
|
||||
import types
|
||||
import logging
|
||||
import binascii
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.exceptions import SaltException
|
||||
|
@ -173,7 +173,7 @@ class IPv6AddressScoped(ipaddress.IPv6Address):
|
|||
self._ip = address
|
||||
elif self._is_packed_binary(address):
|
||||
self._check_packed_address(address, 16)
|
||||
self._ip = ipaddress._int_from_bytes(address, 'big')
|
||||
self._ip = int(binascii.hexlify(address), 16)
|
||||
else:
|
||||
address = str(address)
|
||||
if '/' in address:
|
||||
|
@ -190,7 +190,7 @@ class IPv6AddressScoped(ipaddress.IPv6Address):
|
|||
packed = False
|
||||
if isinstance(data, bytes) and len(data) == 16 and b':' not in data:
|
||||
try:
|
||||
packed = bool(int(str(bytearray(data)).encode('hex'), 16))
|
||||
packed = bool(int(binascii.hexlify(data), 16))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
|
|
@ -1828,7 +1828,7 @@ def create_disk(kwargs=None, call=None):
|
|||
)
|
||||
return False
|
||||
|
||||
if 'size' is None and 'image' is None and 'snapshot' is None:
|
||||
if size is None and image is None and snapshot is None:
|
||||
log.error(
|
||||
'Must specify image, snapshot, or size.'
|
||||
)
|
||||
|
|
|
@ -157,14 +157,13 @@ def _windows_disks():
|
|||
|
||||
namespace = r'\\root\microsoft\windows\storage'
|
||||
path = 'MSFT_PhysicalDisk'
|
||||
where = '(MediaType=3 or MediaType=4)'
|
||||
get = 'DeviceID,MediaType'
|
||||
|
||||
ret = {'disks': [], 'SSDs': []}
|
||||
|
||||
cmdret = __salt__['cmd.run_all'](
|
||||
'{0} /namespace:{1} path {2} where {3} get {4} /format:table'.format(
|
||||
wmic, namespace, path, where, get))
|
||||
'{0} /namespace:{1} path {2} get {3} /format:table'.format(
|
||||
wmic, namespace, path, get))
|
||||
|
||||
if cmdret['retcode'] != 0:
|
||||
log.trace('Disk grain does not support this version of Windows')
|
||||
|
@ -181,10 +180,12 @@ def _windows_disks():
|
|||
elif mediatype == '4':
|
||||
log.trace('Device %s reports itself as an SSD', device)
|
||||
ret['SSDs'].append(device)
|
||||
ret['disks'].append(device)
|
||||
elif mediatype == '5':
|
||||
log.trace('Device %s reports itself as an SCM', device)
|
||||
ret['disks'].append(device)
|
||||
else:
|
||||
log.trace(
|
||||
'Unable to identify device %s as an SSD or HDD. It does '
|
||||
'not report 3 or 4', device
|
||||
)
|
||||
log.trace('Device %s reports itself as Unspecified', device)
|
||||
ret['disks'].append(device)
|
||||
|
||||
return ret
|
||||
|
|
|
@ -1256,9 +1256,9 @@ def edit_team(name,
|
|||
parameters = {}
|
||||
if name is not None:
|
||||
parameters['name'] = name
|
||||
if 'description' is not None:
|
||||
if description is not None:
|
||||
parameters['description'] = description
|
||||
if 'privacy' is not None:
|
||||
if privacy is not None:
|
||||
parameters['privacy'] = privacy
|
||||
if permission is not None:
|
||||
parameters['permission'] = permission
|
||||
|
|
|
@ -1071,7 +1071,7 @@ def remove(path, force=False):
|
|||
raise SaltInvocationError('File path must be absolute: {0}'.format(path))
|
||||
|
||||
# Does the file/folder exists
|
||||
if not os.path.exists(path):
|
||||
if not os.path.exists(path) and not is_link(path):
|
||||
raise CommandExecutionError('Path not found: {0}'.format(path))
|
||||
|
||||
# Remove ReadOnly Attribute
|
||||
|
|
|
@ -1722,16 +1722,21 @@ class Keys(LowDataAdapter):
|
|||
priv_key_file = tarfile.TarInfo('minion.pem')
|
||||
priv_key_file.size = len(priv_key)
|
||||
|
||||
fileobj = six.StringIO()
|
||||
fileobj = BytesIO()
|
||||
tarball = tarfile.open(fileobj=fileobj, mode='w')
|
||||
tarball.addfile(pub_key_file, six.StringIO(pub_key))
|
||||
tarball.addfile(priv_key_file, six.StringIO(priv_key))
|
||||
|
||||
if six.PY3:
|
||||
pub_key = pub_key.encode(__salt_system_encoding__)
|
||||
priv_key = priv_key.encode(__salt_system_encoding__)
|
||||
|
||||
tarball.addfile(pub_key_file, BytesIO(pub_key))
|
||||
tarball.addfile(priv_key_file, BytesIO(priv_key))
|
||||
tarball.close()
|
||||
|
||||
headers = cherrypy.response.headers
|
||||
headers['Content-Disposition'] = 'attachment; filename="saltkeys-{0}.tar"'.format(lowstate[0]['id_'])
|
||||
headers['Content-Type'] = 'application/x-tar'
|
||||
headers['Content-Length'] = fileobj.len
|
||||
headers['Content-Length'] = len(fileobj.getvalue())
|
||||
headers['Cache-Control'] = 'no-cache'
|
||||
|
||||
fileobj.seek(0)
|
||||
|
|
|
@ -4189,7 +4189,7 @@ def replace(name,
|
|||
|
||||
pattern
|
||||
A regular expression, to be matched using Python's
|
||||
:py:func:`~re.search`.
|
||||
:py:func:`re.search`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
87
tests/unit/grains/test_disks.py
Normal file
87
tests/unit/grains/test_disks.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Shane Lee <slee@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import textwrap
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
patch,
|
||||
MagicMock,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.grains.disks as disks
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class IscsiGrainsTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for _windows_disks grains
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {
|
||||
disks: {
|
||||
'__salt__': {},
|
||||
},
|
||||
}
|
||||
|
||||
def test__windows_disks(self):
|
||||
'''
|
||||
Test grains._windows_disks, normal return
|
||||
Should return a populated dictionary
|
||||
'''
|
||||
mock_which = MagicMock(return_value='C:\\Windows\\System32\\wbem\\WMIC.exe')
|
||||
wmic_result = textwrap.dedent('''
|
||||
DeviceId MediaType
|
||||
0 4
|
||||
1 0
|
||||
2 3
|
||||
3 5
|
||||
''')
|
||||
mock_run_all = MagicMock(return_value={'stdout': wmic_result,
|
||||
'retcode': 0})
|
||||
|
||||
with patch('salt.utils.path.which', mock_which), \
|
||||
patch.dict(disks.__salt__, {'cmd.run_all': mock_run_all}):
|
||||
result = disks._windows_disks()
|
||||
expected = {
|
||||
'SSDs': ['\\\\.\\PhysicalDrive0'],
|
||||
'disks': [
|
||||
'\\\\.\\PhysicalDrive0',
|
||||
'\\\\.\\PhysicalDrive1',
|
||||
'\\\\.\\PhysicalDrive2',
|
||||
'\\\\.\\PhysicalDrive3']}
|
||||
self.assertDictEqual(result, expected)
|
||||
cmd = ' '.join([
|
||||
'C:\\Windows\\System32\\wbem\\WMIC.exe',
|
||||
'/namespace:\\\\root\\microsoft\\windows\\storage',
|
||||
'path',
|
||||
'MSFT_PhysicalDisk',
|
||||
'get',
|
||||
'DeviceID,MediaType',
|
||||
'/format:table'
|
||||
])
|
||||
mock_run_all.assert_called_once_with(cmd)
|
||||
|
||||
def test__windows_disks_retcode(self):
|
||||
'''
|
||||
Test grains._windows_disks, retcode 1
|
||||
Should return empty lists
|
||||
'''
|
||||
mock_which = MagicMock(return_value='C:\\Windows\\System32\\wbem\\WMIC.exe')
|
||||
mock_run_all = MagicMock(return_value={'stdout': '',
|
||||
'retcode': 1})
|
||||
with patch('salt.utils.path.which', mock_which), \
|
||||
patch.dict(disks.__salt__, {'cmd.run_all': mock_run_all}):
|
||||
result = disks._windows_disks()
|
||||
expected = {
|
||||
'SSDs': [],
|
||||
'disks': []}
|
||||
self.assertDictEqual(result, expected)
|
|
@ -19,6 +19,7 @@ from tests.support.helpers import destructiveTest
|
|||
|
||||
# Import Salt Libs
|
||||
import salt.modules.win_file as win_file
|
||||
import salt.modules.temp as temp
|
||||
from salt.exceptions import CommandExecutionError
|
||||
import salt.utils.platform
|
||||
import salt.utils.win_functions
|
||||
|
@ -307,3 +308,19 @@ class WinFileCheckPermsTestCase(TestCase, LoaderModuleMockMixin):
|
|||
inheritance=False,
|
||||
reset=True)
|
||||
self.assertDictEqual(expected, ret)
|
||||
|
||||
def test_issue_52002_check_file_remove_symlink(self):
|
||||
'''
|
||||
Make sure that directories including symlinks or symlinks can be removed
|
||||
'''
|
||||
base = temp.dir(prefix='base')
|
||||
target = os.path.join(base, 'child 1', 'target/')
|
||||
symlink = os.path.join(base, 'child 2', 'link')
|
||||
self.assertFalse(win_file.directory_exists(target))
|
||||
self.assertFalse(win_file.directory_exists(symlink))
|
||||
self.assertTrue(win_file.makedirs_(target))
|
||||
self.assertTrue(win_file.directory_exists(symlink))
|
||||
self.assertTrue(win_file.symlink(target, symlink))
|
||||
self.assertTrue(win_file.is_link(symlink))
|
||||
self.assertTrue(win_file.remove(base))
|
||||
self.assertFalse(win_file.directory_exists(base))
|
||||
|
|
|
@ -69,3 +69,15 @@ class CompatTestCase(TestCase):
|
|||
else:
|
||||
expected = 'StringIO.StringIO instance'
|
||||
self.assertTrue(expected in repr(ret))
|
||||
|
||||
def test_ipv6_class__is_packed_binary(self):
|
||||
ipv6 = compat.IPv6AddressScoped('2001:db8::')
|
||||
self.assertEqual(str(ipv6), '2001:db8::')
|
||||
|
||||
def test_ipv6_class__is_packed_binary_integer(self):
|
||||
ipv6 = compat.IPv6AddressScoped(42540766411282592856903984951653826560)
|
||||
self.assertEqual(str(ipv6), '2001:db8::')
|
||||
|
||||
def test_ipv6_class__is_packed_binary__issue_51831(self):
|
||||
ipv6 = compat.IPv6AddressScoped(b'sixteen.digit.bn')
|
||||
self.assertEqual(str(ipv6), '7369:7874:6565:6e2e:6469:6769:742e:626e')
|
||||
|
|
Loading…
Add table
Reference in a new issue