salt/tests/unit/modules/glusterfs_test.py
Joe Julian 01a8e7ee10 Convert glusterfs module to use xml
The gluster cli has the option to produce xml output from most every
command, and to avoid pausing for questions when being run in script
mode. Make use of that capability and avoid scraping text output that is
often subject to change between versions.

This should help future-proof this module as any changes to the xml
output should be automatically integrated.

As part of this change, I added a glusterfs.info function and added the
force keywords to volume start and volume stop.

Fixes #30051
2016-01-04 09:47:59 -08:00

443 lines
15 KiB
Python

# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
:codeauthor: :email:`Joe Julian <me@joejulian.name>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.mock import (
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
# Import Salt Libs
from salt.modules import glusterfs
import salt.utils.cloud as suc
from salt.exceptions import CommandExecutionError, SaltInvocationError
# Globals
glusterfs.__salt__ = {}
xml_peer_present = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
<peer>
<hostname>node02</hostname>
</peer>
</cliOutput>
"""
xml_volume_present = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
<volList>
<volume>Newvolume1</volume>
<volume>Newvolume2</volume>
</volList>
</cliOutput>
"""
xml_volume_absent = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
<volList>
<count>0</count>
</volList>
</cliOutput>
"""
xml_volume_status = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
<volStatus>
<volumes>
<volume>
<volName>myvol1</volName>
<nodeCount>3</nodeCount>
<node>
<hostname>node01</hostname>
<path>/tmp/foo</path>
<peerid>830700d7-0684-497c-a12c-c02e365fb90b</peerid>
<status>1</status>
<port>49155</port>
<ports>
<tcp>49155</tcp>
<rdma>N/A</rdma>
</ports>
<pid>2470</pid>
</node>
<node>
<hostname>NFS Server</hostname>
<path>localhost</path>
<peerid>830700d7-0684-497c-a12c-c02e365fb90b</peerid>
<status>0</status>
<port>N/A</port>
<ports>
<tcp>N/A</tcp>
<rdma>N/A</rdma>
</ports>
<pid>-1</pid>
</node>
<tasks/>
</volume>
</volumes>
</volStatus>
</cliOutput>
"""
xml_volume_info_running = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
<volInfo>
<volumes>
<volume>
<name>myvol1</name>
<id>f03c2180-cf55-4f77-ae0b-3650f57c82a1</id>
<status>1</status>
<statusStr>Started</statusStr>
<brickCount>1</brickCount>
<distCount>1</distCount>
<stripeCount>1</stripeCount>
<replicaCount>1</replicaCount>
<disperseCount>0</disperseCount>
<redundancyCount>0</redundancyCount>
<type>0</type>
<typeStr>Distribute</typeStr>
<transport>0</transport>
<bricks>
<brick uuid="830700d7-0684-497c-a12c-c02e365fb90b">node01:/tmp/foo<name>node01:/tmp/foo</name><hostUuid>830700d7-0684-497c-a12c-c02e365fb90b</hostUuid></brick>
</bricks>
<optCount>1</optCount>
<options>
<option>
<name>performance.readdir-ahead</name>
<value>on</value>
</option>
</options>
</volume>
<count>1</count>
</volumes>
</volInfo>
</cliOutput>
"""
xml_volume_info_stopped = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
<volInfo>
<volumes>
<volume>
<name>myvol1</name>
<status>1</status>
</volume>
</volumes>
</volInfo>
</cliOutput>
"""
xml_command_success = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>0</opRet>
</cliOutput>
"""
xml_command_fail = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cliOutput>
<opRet>-1</opRet>
<opErrno>0</opErrno>
<opErrstr>Command Failed</opErrstr>
</cliOutput>
"""
@skipIf(NO_MOCK, NO_MOCK_REASON)
class GlusterfsTestCase(TestCase):
'''
Test cases for salt.modules.glusterfs
'''
# 'list_peers' function tests: 1
def test_list_peers(self):
'''
Test if it return a list of gluster peers
'''
mock = MagicMock(return_value=xml_peer_present)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertListEqual(glusterfs.list_peers(), ['node02'])
mock = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertIsNone(glusterfs.list_peers())
# 'peer' function tests: 1
def test_peer(self):
'''
Test if it adds another node into the peer list.
'''
mock = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertTrue(glusterfs.peer('salt'))
mock = MagicMock(return_value=True)
with patch.object(suc, 'check_name', mock):
self.assertRaises(SaltInvocationError, glusterfs.peer, 'a')
# 'create' function tests: 1
def test_create(self):
'''
Test if it create a glusterfs volume.
'''
mock = MagicMock(return_value='')
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
SaltInvocationError, glusterfs.create, 'newvolume', 'host1:brick')
mock = MagicMock(return_value='')
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
SaltInvocationError, glusterfs.create, 'newvolume', 'host1/brick')
mock = MagicMock(return_value=xml_command_fail)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(CommandExecutionError, glusterfs.create, 'newvolume', 'host1:/brick',
True, True, True, 'tcp', True)
mock = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.create('newvolume', 'host1:/brick',
True, True, True, 'tcp', True),
'Volume newvolume created and started')
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.create('newvolume', 'host1:/brick'),
'Volume newvolume created. Start volume to use')
# 'list_volumes' function tests: 1
def test_list_volumes(self):
'''
Test if it list configured volumes
'''
mock = MagicMock(return_value=xml_volume_absent)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertListEqual(glusterfs.list_volumes(), [])
mock = MagicMock(return_value=xml_volume_present)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertListEqual(glusterfs.list_volumes(),
['Newvolume1', 'Newvolume2'])
# 'status' function tests: 1
def test_status(self):
'''
Test if it check the status of a gluster volume.
'''
mock = MagicMock(return_value=xml_command_fail)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
CommandExecutionError, glusterfs.status, 'myvol1')
res = {'bricks': {
'node01:/tmp/foo': {
'host': 'node01',
'hostname': 'node01',
'online': True,
'path': '/tmp/foo',
'peerid': '830700d7-0684-497c-a12c-c02e365fb90b',
'pid': '2470',
'port': '49155',
'ports': {
'rdma': 'N/A',
'tcp': '49155'},
'status': '1'}},
'healers': {},
'nfs': {
'node01': {
'host': 'NFS Server',
'hostname': 'NFS Server',
'online': False,
'path': 'localhost',
'peerid': '830700d7-0684-497c-a12c-c02e365fb90b',
'pid': '-1',
'port': 'N/A',
'ports': {
'rdma': 'N/A',
'tcp': 'N/A'},
'status': '0'}}}
mock = MagicMock(return_value=xml_volume_status)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertDictEqual(glusterfs.status('myvol1'), res)
# 'start_volume' function tests: 1
def test_volume_info(self):
'''
Test if it returns the volume info.
'''
res = {'myvol1': {
'brickCount': '1',
'bricks': {
'brick1': {
'hostUuid': '830700d7-0684-497c-a12c-c02e365fb90b',
'path': 'node01:/tmp/foo',
'uuid': '830700d7-0684-497c-a12c-c02e365fb90b'}},
'disperseCount': '0',
'distCount': '1',
'id': 'f03c2180-cf55-4f77-ae0b-3650f57c82a1',
'name': 'myvol1',
'optCount': '1',
'options': {
'performance.readdir-ahead': 'on'},
'redundancyCount': '0',
'replicaCount': '1',
'status': '1',
'statusStr': 'Started',
'stripeCount': '1',
'transport': '0',
'type': '0',
'typeStr': 'Distribute'}}
mock = MagicMock(return_value=xml_volume_info_running)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertDictEqual(glusterfs.info('myvol1'), res)
def test_start_volume(self):
'''
Test if it start a gluster volume.
'''
mock_list = MagicMock(return_value=['Newvolume1', 'Newvolume2'])
with patch.object(glusterfs, 'list_volumes', mock_list):
mock_status = MagicMock(return_value={'status': '1'})
with patch.object(glusterfs, 'info', mock_status):
mock = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.start_volume('Newvolume1'),
'Volume already started')
mock_status = MagicMock(return_value={'status': '0'})
with patch.object(glusterfs, 'info', mock_status):
mock_run = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}):
self.assertEqual(glusterfs.start_volume('Newvolume1'),
'Volume Newvolume1 started')
mock = MagicMock(return_value=xml_command_fail)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
CommandExecutionError, glusterfs.start_volume, 'Newvolume1')
# 'stop_volume' function tests: 1
def test_stop_volume(self):
'''
Test if it stop a gluster volume.
'''
mock = MagicMock(return_value={})
with patch.object(glusterfs, 'status', mock):
mock = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.stop_volume('Newvolume1'),
'Volume Newvolume1 stopped')
mock = MagicMock(return_value=xml_command_fail)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
CommandExecutionError, glusterfs.stop_volume, 'Newvolume1')
mock = MagicMock(return_value=xml_command_fail)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
CommandExecutionError, glusterfs.stop_volume, 'Newvolume1')
# 'delete' function tests: 1
def test_delete(self):
'''
Test if it deletes a gluster volume.
'''
mock = MagicMock(return_value=['Newvolume1', 'Newvolume2'])
with patch.object(glusterfs, 'list_volumes', mock):
# volume doesn't exist
self.assertRaises(
SaltInvocationError, glusterfs.delete, 'Newvolume3')
mock = MagicMock(return_value={'status': '1'})
with patch.object(glusterfs, 'info', mock):
mock = MagicMock(return_value=xml_command_success)
# volume exists, should not be stopped, and is started
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
SaltInvocationError, glusterfs.delete, 'Newvolume1', False)
# volume exists, should be stopped, and is started
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.delete('Newvolume1'),
'Volume Newvolume1 stopped and deleted')
# volume exists and isn't started
mock = MagicMock(return_value={'status': '0'})
with patch.object(glusterfs, 'info', mock):
mock = MagicMock(return_value=xml_command_success)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.delete('Newvolume1'),
'Volume Newvolume1 deleted')
# 'add_volume_bricks' function tests: 1
def test_add_volume_bricks(self):
'''
Test if it add brick(s) to an existing volume
'''
# volume does not exist
mock = MagicMock(return_value=xml_command_fail)
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertRaises(
CommandExecutionError, glusterfs.add_volume_bricks, 'Newvolume1', ['bricks'])
ret = '1 bricks successfully added to the volume Newvolume1'
# volume does exist
mock = MagicMock(return_value={'bricks': {}})
with patch.object(glusterfs, 'info', mock):
mock = MagicMock(return_value=xml_command_success)
# ... and the added brick does not exist
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1',
['bricks']), ret)
mock = MagicMock(
return_value={'bricks': {'brick1': {'path': 'bricks'}}})
with patch.object(glusterfs, 'info', mock):
# ... and the added brick does exist
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
# As a list
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1', ['bricks']),
'Bricks already in volume Newvolume1')
# As a string
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1', 'bricks'),
'Bricks already in volume Newvolume1')
# And empty list
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1', []),
'Bricks already in volume Newvolume1')
if __name__ == '__main__':
from integration import run_tests
run_tests(GlusterfsTestCase, needs_daemon=False)