Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[master] Porting #50953 to master #54548

Merged
merged 2 commits into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/ref/configuration/minion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,20 @@ minion. Since this grain is expensive, it is disabled by default.

iscsi_grains: True

.. conf_minion:: nvme_grains

``nvme_grains``
------------------------

Default: ``False``

The ``nvme_grains`` setting will enable the ``nvme_nqn`` grain on the
minion. Since this grain is expensive, it is disabled by default.

.. code-block:: yaml

nvme_grains: True

.. conf_minion:: mine_enabled

``mine_enabled``
Expand Down
64 changes: 64 additions & 0 deletions salt/grains/nvme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
'''
Grains for NVMe Qualified Names (NQN).

.. versionadded:: Flourine

To enable these grains set `nvme_grains: True`.

.. code-block:: yaml

nvme_grains: True
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals

import errno
import logging

# Import Salt libs
import salt.utils.files
import salt.utils.path
import salt.utils.platform

__virtualname__ = 'nvme'

# Get logging started
log = logging.getLogger(__name__)


def __virtual__():
if __opts__.get('nvme_grains', False) is False:
return False
return __virtualname__


def nvme_nqn():
'''
Return NVMe NQN
'''
grains = {}
grains['nvme_nqn'] = False
if salt.utils.platform.is_linux():
grains['nvme_nqn'] = _linux_nqn()
return grains


def _linux_nqn():
'''
Return NVMe NQN from a Linux host.
'''
ret = []

initiator = '/etc/nvme/hostnqn'
try:
with salt.utils.files.fopen(initiator, 'r') as _nvme:
for line in _nvme:
line = line.strip()
if line.startswith('nqn.'):
ret.append(line)
except IOError as ex:
if ex.errno != errno.ENOENT:
log.debug("Error while accessing '%s': %s", initiator, ex)

return ret
67 changes: 67 additions & 0 deletions tests/unit/grains/test_nvme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Simon Dodsley <simon@purestorage.com>`
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import errno
import textwrap

# Import Salt Testing Libs
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
patch,
mock_open,
MagicMock,
NO_MOCK,
NO_MOCK_REASON
)

# Import Salt Libs
import salt.grains.nvme as nvme


@skipIf(NO_MOCK, NO_MOCK_REASON)
class NvmeGrainsTestCase(TestCase):
'''
Test cases for nvme grains
'''

def test_linux_nvme_nqn_grains(self):
_nvme_file = textwrap.dedent('''\
nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d
''')

with patch('salt.utils.files.fopen', mock_open(read_data=_nvme_file)):
nqn = nvme._linux_nqn()

assert isinstance(nqn, list)
assert len(nqn) == 1
assert nqn == ['nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d']

@patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.EPERM,
'The cables are not the same length.')))
@patch('salt.grains.nvme.log', MagicMock())
def test_linux_nqn_non_root(self):
'''
Test if linux_nqn is running on salt-master as non-root
and handling access denial properly.
:return:
'''
assert nvme._linux_nqn() == []
nvme.log.debug.assert_called()
assert 'Error while accessing' in nvme.log.debug.call_args[0][0]
assert 'cables are not the same' in nvme.log.debug.call_args[0][2].strerror
assert nvme.log.debug.call_args[0][2].errno == errno.EPERM
assert nvme.log.debug.call_args[0][1] == '/etc/nvme/hostnqn'

@patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.ENOENT, '')))
@patch('salt.grains.nvme.log', MagicMock())
def test_linux_nqn_no_nvme_initiator(self):
'''
Test if linux_nqn is running on salt-master as root.
nvme initiator is not there accessible or is not supported.
:return:
'''
assert nvme._linux_nqn() == []
nvme.log.debug.assert_not_called()