mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #30075 from iodatacenters/2015.8_gluster_usexml
Convert glusterfs module to use xml
This commit is contained in:
commit
5419699bd2
2 changed files with 460 additions and 234 deletions
|
@ -6,16 +6,17 @@ from __future__ import absolute_import
|
|||
|
||||
# Import python libs
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# Import 3rd-party libs
|
||||
# pylint: disable=import-error,redefined-builtin
|
||||
from salt.ext.six.moves import range
|
||||
from salt.ext.six.moves import shlex_quote as _cmd_quote
|
||||
# pylint: enable=import-error,redefined-builtin
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.cloud as suc
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -41,6 +42,40 @@ def _get_minor_version():
|
|||
return version
|
||||
|
||||
|
||||
def _gluster(cmd):
|
||||
'''
|
||||
Perform a gluster command.
|
||||
'''
|
||||
# We will pass the command string as stdin to allow for much longer
|
||||
# command strings. This is especially useful for creating large volumes
|
||||
# where the list of bricks exceeds 128 characters.
|
||||
return __salt__['cmd.run'](
|
||||
'gluster --mode=script', stdin="{0}\n".format(cmd))
|
||||
|
||||
|
||||
def _gluster_xml(cmd):
|
||||
'''
|
||||
Perform a gluster --xml command and check for and raise errors.
|
||||
'''
|
||||
root = ET.fromstring(
|
||||
__salt__['cmd.run'](
|
||||
'gluster --xml --mode=script', stdin="{0}\n".format(cmd)
|
||||
).replace("\n", ""))
|
||||
if int(root.find('opRet').text) != 0:
|
||||
raise CommandExecutionError(root.find('opErrstr').text)
|
||||
return root
|
||||
|
||||
|
||||
def _etree_to_dict(t):
|
||||
if len(t.getchildren()) > 0:
|
||||
d = {}
|
||||
for child in t.getchildren():
|
||||
d[child.tag] = _etree_to_dict(child)
|
||||
else:
|
||||
d = t.text
|
||||
return d
|
||||
|
||||
|
||||
def list_peers():
|
||||
'''
|
||||
Return a list of gluster peers
|
||||
|
@ -70,12 +105,12 @@ def list_peers():
|
|||
|
||||
|
||||
'''
|
||||
get_peer_list = 'gluster peer status | awk \'/Hostname/ {print $2}\''
|
||||
result = __salt__['cmd.run'](get_peer_list, python_shell=True)
|
||||
if 'No peers present' in result:
|
||||
root = _gluster_xml('peer status')
|
||||
result = [x.find('hostname').text for x in root.iter('peer')]
|
||||
if len(result) == 0:
|
||||
return None
|
||||
else:
|
||||
return result.splitlines()
|
||||
return result
|
||||
|
||||
|
||||
def peer(name):
|
||||
|
@ -109,10 +144,11 @@ def peer(name):
|
|||
|
||||
'''
|
||||
if suc.check_name(name, 'a-zA-Z0-9._-'):
|
||||
return 'Invalid characters in peer name'
|
||||
raise SaltInvocationError(
|
||||
'Invalid characters in peer name "{0}"'.format(name))
|
||||
|
||||
cmd = 'gluster peer probe {0}'.format(name)
|
||||
return __salt__['cmd.run'](cmd)
|
||||
cmd = 'peer probe {0}'.format(name)
|
||||
return _gluster_xml(cmd).find('opRet').text == '0'
|
||||
|
||||
|
||||
def create(name, bricks, stripe=False, replica=False, device_vg=False,
|
||||
|
@ -165,20 +201,22 @@ def create(name, bricks, stripe=False, replica=False, device_vg=False,
|
|||
|
||||
# Error for block devices with multiple bricks
|
||||
if device_vg and len(bricks) > 1:
|
||||
return 'Error: Block device backend volume does not support multipl' +\
|
||||
'bricks'
|
||||
raise SaltInvocationError('Block device backend volume does not ' +
|
||||
'support multiple bricks')
|
||||
|
||||
# Validate bricks syntax
|
||||
for brick in bricks:
|
||||
try:
|
||||
peer_name, path = brick.split(':')
|
||||
if not path.startswith('/'):
|
||||
return 'Error: Brick paths must start with /'
|
||||
raise SaltInvocationError(
|
||||
'Brick paths must start with / in {0}'.format(brick))
|
||||
except ValueError:
|
||||
return 'Error: Brick syntax is <peer>:<path>'
|
||||
raise SaltInvocationError(
|
||||
'Brick syntax is <peer>:<path> got {0}'.format(brick))
|
||||
|
||||
# Format creation call
|
||||
cmd = 'gluster volume create {0} '.format(name)
|
||||
cmd = 'volume create {0} '.format(name)
|
||||
if stripe:
|
||||
cmd += 'stripe {0} '.format(stripe)
|
||||
if replica:
|
||||
|
@ -192,16 +230,11 @@ def create(name, bricks, stripe=False, replica=False, device_vg=False,
|
|||
cmd += ' force'
|
||||
|
||||
log.debug('Clustering command:\n{0}'.format(cmd))
|
||||
ret = __salt__['cmd.run'](cmd)
|
||||
if 'failed' in ret:
|
||||
return ret
|
||||
_gluster_xml(cmd)
|
||||
|
||||
if start:
|
||||
result = __salt__['cmd.run']('gluster volume start {0}'.format(name))
|
||||
if result.endswith('success'):
|
||||
return 'Volume {0} created and started'.format(name)
|
||||
else:
|
||||
return result
|
||||
_gluster_xml('gluster volume start {0}'.format(name))
|
||||
return 'Volume {0} created and started'.format(name)
|
||||
else:
|
||||
return 'Volume {0} created. Start volume to use'.format(name)
|
||||
|
||||
|
@ -217,11 +250,10 @@ def list_volumes():
|
|||
salt '*' glusterfs.list_volumes
|
||||
'''
|
||||
|
||||
results = __salt__['cmd.run']('gluster volume list').splitlines()
|
||||
if results[0] == 'No volumes present in cluster':
|
||||
return []
|
||||
else:
|
||||
return results
|
||||
get_volume_list = 'gluster --xml volume list'
|
||||
root = _gluster_xml('volume list')
|
||||
results = [x.text for x in root.iter('volume')]
|
||||
return results
|
||||
|
||||
|
||||
def status(name):
|
||||
|
@ -237,126 +269,140 @@ def status(name):
|
|||
|
||||
salt '*' glusterfs.status myvolume
|
||||
'''
|
||||
# Get minor version
|
||||
minor_version = _get_minor_version()
|
||||
# Get volume status
|
||||
cmd = 'gluster volume status {0}'.format(name)
|
||||
result = __salt__['cmd.run'](cmd).splitlines()
|
||||
if 'does not exist' in result[0]:
|
||||
return result[0]
|
||||
if 'is not started' in result[0]:
|
||||
return result[0]
|
||||
root = _gluster_xml('volume status {0}'.format(name))
|
||||
|
||||
ret = {'bricks': {}, 'nfs': {}, 'healers': {}}
|
||||
# Iterate line by line, concatenating lines the gluster cli separated
|
||||
for line_number in range(len(result)):
|
||||
line = result[line_number]
|
||||
if line.startswith('Brick'):
|
||||
# See if this line is broken up into multiple lines
|
||||
while len(line.split()) < 5:
|
||||
line_number = line_number + 1
|
||||
line = line.rstrip() + result[line_number]
|
||||
|
||||
# Parse Brick data
|
||||
if minor_version >= 7:
|
||||
brick, port, port_rdma, online, pid = line.split()[1:]
|
||||
else:
|
||||
brick, port, online, pid = line.split()[1:]
|
||||
host, path = brick.split(':')
|
||||
data = {'port': port, 'pid': pid, 'host': host, 'path': path}
|
||||
if online == 'Y':
|
||||
data['online'] = True
|
||||
else:
|
||||
data['online'] = False
|
||||
# Store, keyed by <host>:<brick> string
|
||||
ret['bricks'][brick] = data
|
||||
elif line.startswith('NFS Server on'):
|
||||
# See if this line is broken up into multiple lines
|
||||
while len(line.split()) < 5:
|
||||
line_number = line_number + 1
|
||||
line = line.rstrip() + result[line_number]
|
||||
def etree_legacy_wrap(t):
|
||||
ret = _etree_to_dict(t)
|
||||
ret['online'] = (ret['status'] == '1')
|
||||
ret['host'] = ret['hostname']
|
||||
return ret
|
||||
|
||||
# Parse NFS Server data
|
||||
if minor_version >= 7:
|
||||
host, port, port_rdma, online, pid = line.split()[3:]
|
||||
else:
|
||||
host, port, online, pid = line.split()[3:]
|
||||
data = {'port': port, 'pid': pid}
|
||||
if online == 'Y':
|
||||
data['online'] = True
|
||||
else:
|
||||
data['online'] = False
|
||||
# Store, keyed by hostname
|
||||
ret['nfs'][host] = data
|
||||
elif line.startswith('Self-heal Daemon on'):
|
||||
# See if this line is broken up into multiple lines
|
||||
while len(line.split()) < 5:
|
||||
line_number = line_number + 1
|
||||
line = line.rstrip() + result[line_number]
|
||||
# Build a hash to map hostname to peerid
|
||||
hostref = {}
|
||||
for node in root.iter('node'):
|
||||
peerid = node.find('peerid').text
|
||||
hostname = node.find('hostname').text
|
||||
if hostname not in ('NFS Server', 'Self-heal Daemon'):
|
||||
hostref[peerid] = hostname
|
||||
|
||||
for node in root.iter('node'):
|
||||
hostname = node.find('hostname').text
|
||||
if hostname not in ('NFS Server', 'Self-heal Daemon'):
|
||||
path = node.find('path').text
|
||||
ret['bricks'][
|
||||
'{0}:{1}'.format(hostname, path)] = etree_legacy_wrap(node)
|
||||
elif hostname == 'NFS Server':
|
||||
peerid = node.find('peerid').text
|
||||
true_hostname = hostref[peerid]
|
||||
ret['nfs'][true_hostname] = etree_legacy_wrap(node)
|
||||
else:
|
||||
peerid = node.find('peerid').text
|
||||
true_hostname = hostref[peerid]
|
||||
ret['healers'][true_hostname] = etree_legacy_wrap(node)
|
||||
|
||||
# Parse NFS Server data
|
||||
if minor_version >= 7:
|
||||
host, port, port_rdma, online, pid = line.split()[3:]
|
||||
else:
|
||||
host, port, online, pid = line.split()[3:]
|
||||
data = {'port': port, 'pid': pid}
|
||||
if online == 'Y':
|
||||
data['online'] = True
|
||||
else:
|
||||
data['online'] = False
|
||||
# Store, keyed by hostname
|
||||
ret['healers'][host] = data
|
||||
return ret
|
||||
|
||||
|
||||
def start_volume(name):
|
||||
def info(name):
|
||||
'''
|
||||
.. versionadded:: 2015.8.4
|
||||
|
||||
Return the gluster volume info.
|
||||
|
||||
name
|
||||
Volume name
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' glusterfs.info myvolume
|
||||
|
||||
'''
|
||||
cmd = 'volume info {0}'.format(name)
|
||||
root = _gluster_xml(cmd)
|
||||
|
||||
volume = [x for x in root.iter('volume')][0]
|
||||
|
||||
ret = {name: _etree_to_dict(volume)}
|
||||
|
||||
bricks = {}
|
||||
for i, brick in enumerate(volume.iter('brick'), start=1):
|
||||
brickkey = 'brick{0}'.format(i)
|
||||
bricks[brickkey] = {'path': brick.text}
|
||||
for child in brick.getchildren():
|
||||
if not child.tag == 'name':
|
||||
bricks[brickkey].update({child.tag: child.text})
|
||||
for k, v in brick.items():
|
||||
bricks[brickkey][k] = v
|
||||
ret[name]['bricks'] = bricks
|
||||
|
||||
options = {}
|
||||
for option in volume.iter('option'):
|
||||
options[option.find('name').text] = option.find('value').text
|
||||
ret[name]['options'] = options
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def start_volume(name, force=False):
|
||||
'''
|
||||
Start a gluster volume.
|
||||
|
||||
name
|
||||
Volume name
|
||||
|
||||
force
|
||||
Force the volume start even if the volume is started
|
||||
.. versionadded:: 2015.8.4
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' glusterfs.start mycluster
|
||||
'''
|
||||
volumes = list_volumes()
|
||||
if name in volumes:
|
||||
if isinstance(status(name), dict):
|
||||
return 'Volume already started'
|
||||
cmd = 'gluster volume start {0}'.format(name)
|
||||
result = __salt__['cmd.run'](cmd)
|
||||
if result.endswith('success'):
|
||||
return 'Volume {0} started'.format(name)
|
||||
else:
|
||||
return result
|
||||
return 'Volume does not exist'
|
||||
cmd = 'volume start {0}'.format(name)
|
||||
if force:
|
||||
cmd = '{0} force'.format(cmd)
|
||||
|
||||
volinfo = info(name)
|
||||
|
||||
if not force and volinfo['status'] == '1':
|
||||
return 'Volume already started'
|
||||
|
||||
_gluster_xml(cmd)
|
||||
return 'Volume {0} started'.format(name)
|
||||
|
||||
|
||||
def stop_volume(name):
|
||||
def stop_volume(name, force=False):
|
||||
'''
|
||||
Stop a gluster volume.
|
||||
|
||||
name
|
||||
Volume name
|
||||
|
||||
force
|
||||
Force stop the volume
|
||||
.. versionadded:: 2015.8.4
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' glusterfs.stop_volume mycluster
|
||||
'''
|
||||
vol_status = status(name)
|
||||
if isinstance(vol_status, dict):
|
||||
cmd = 'yes | gluster volume stop {0}'.format(_cmd_quote(name))
|
||||
result = __salt__['cmd.run'](cmd, python_shell=True)
|
||||
if result.splitlines()[0].endswith('success'):
|
||||
return 'Volume {0} stopped'.format(name)
|
||||
else:
|
||||
return result
|
||||
return vol_status
|
||||
status(name)
|
||||
|
||||
cmd = 'volume stop {0}'.format(name)
|
||||
if force:
|
||||
cmd += ' force'
|
||||
|
||||
_gluster_xml(cmd)
|
||||
return 'Volume {0} stopped'.format(name)
|
||||
|
||||
|
||||
def delete(target, stop=True):
|
||||
|
@ -370,28 +416,25 @@ def delete(target, stop=True):
|
|||
Stop volume before delete if it is started, True by default
|
||||
'''
|
||||
if target not in list_volumes():
|
||||
return 'Volume does not exist'
|
||||
|
||||
cmd = 'yes | gluster volume delete {0}'.format(_cmd_quote(target))
|
||||
raise SaltInvocationError('Volume {0} does not exist'.format(target))
|
||||
|
||||
# Stop volume if requested to and it is running
|
||||
if stop is True and isinstance(status(target), dict):
|
||||
stop_volume(target)
|
||||
stopped = True
|
||||
else:
|
||||
stopped = False
|
||||
# Warn volume is running if stop not requested
|
||||
if isinstance(status(target), dict):
|
||||
return 'Error: Volume must be stopped before deletion'
|
||||
running = (info(target)['status'] == '1')
|
||||
|
||||
result = __salt__['cmd.run'](cmd, python_shell=True)
|
||||
if result.splitlines()[0].endswith('success'):
|
||||
if stopped:
|
||||
return 'Volume {0} stopped and deleted'.format(target)
|
||||
else:
|
||||
return 'Volume {0} deleted'.format(target)
|
||||
if not stop and running:
|
||||
# Fail if volume is running if stop is not requested
|
||||
raise SaltInvocationError(
|
||||
'Volume {0} must be stopped before deletion'.format(target))
|
||||
|
||||
if running:
|
||||
stop_volume(target, force=True)
|
||||
|
||||
cmd = 'volume delete {0}'.format(target)
|
||||
_gluster_xml(cmd)
|
||||
if running:
|
||||
return 'Volume {0} stopped and deleted'.format(target)
|
||||
else:
|
||||
return result
|
||||
return 'Volume {0} deleted'.format(target)
|
||||
|
||||
|
||||
def add_volume_bricks(name, bricks):
|
||||
|
@ -407,35 +450,27 @@ def add_volume_bricks(name, bricks):
|
|||
|
||||
new_bricks = []
|
||||
|
||||
cmd = 'echo yes | gluster volume add-brick {0}'.format(name)
|
||||
cmd = 'volume add-brick {0}'.format(name)
|
||||
|
||||
if isinstance(bricks, str):
|
||||
bricks = [bricks]
|
||||
|
||||
volume_bricks = status(name)
|
||||
|
||||
if 'does not exist' in volume_bricks:
|
||||
return volume_bricks
|
||||
|
||||
if 'is not started' in volume_bricks:
|
||||
return volume_bricks
|
||||
volume_bricks = [x['path'] for x in info(name)['bricks'].values()]
|
||||
|
||||
for brick in bricks:
|
||||
if brick in volume_bricks['bricks']:
|
||||
log.debug('Brick {0} already in volume {1}...excluding from command'.format(brick, name))
|
||||
if brick in volume_bricks:
|
||||
log.debug(
|
||||
'Brick {0} already in volume {1}...excluding from command'.format(brick, name))
|
||||
else:
|
||||
new_bricks.append(brick)
|
||||
|
||||
if len(new_bricks) > 0:
|
||||
for brick in new_bricks:
|
||||
cmd += ' '+str(brick)
|
||||
cmd += ' {0}'.format(brick)
|
||||
|
||||
result = __salt__['cmd.run'](cmd)
|
||||
_gluster_xml(cmd)
|
||||
|
||||
if result.endswith('success'):
|
||||
return '{0} bricks successfully added to the volume {1}'.format(len(new_bricks), name)
|
||||
else:
|
||||
return result
|
||||
return '{0} bricks successfully added to the volume {1}'.format(len(new_bricks), name)
|
||||
|
||||
else:
|
||||
return 'Bricks already in volume {0}'.format(name)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
:codeauthor: :email:`Joe Julian <me@joejulian.name>`
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
|
@ -18,13 +19,154 @@ from salttesting.mock import (
|
|||
# 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
|
||||
'''
|
||||
|
@ -34,11 +176,11 @@ class GlusterfsTestCase(TestCase):
|
|||
'''
|
||||
Test if it return a list of gluster peers
|
||||
'''
|
||||
mock = MagicMock(return_value='')
|
||||
mock = MagicMock(return_value=xml_peer_present)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertListEqual(glusterfs.list_peers(), [])
|
||||
self.assertListEqual(glusterfs.list_peers(), ['node02'])
|
||||
|
||||
mock = MagicMock(return_value='No peers present')
|
||||
mock = MagicMock(return_value=xml_command_success)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertIsNone(glusterfs.list_peers())
|
||||
|
||||
|
@ -46,16 +188,15 @@ class GlusterfsTestCase(TestCase):
|
|||
|
||||
def test_peer(self):
|
||||
'''
|
||||
Test if it add another node into the peer list.
|
||||
Test if it adds another node into the peer list.
|
||||
'''
|
||||
mock = MagicMock(return_value='')
|
||||
mock = MagicMock(return_value=xml_command_success)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.peer('salt'), '')
|
||||
self.assertTrue(glusterfs.peer('salt'))
|
||||
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.object(suc, 'check_name', mock):
|
||||
self.assertEqual(glusterfs.peer('a'),
|
||||
'Invalid characters in peer name')
|
||||
self.assertRaises(SaltInvocationError, glusterfs.peer, 'a')
|
||||
|
||||
# 'create' function tests: 1
|
||||
|
||||
|
@ -65,27 +206,25 @@ class GlusterfsTestCase(TestCase):
|
|||
'''
|
||||
mock = MagicMock(return_value='')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.create('newvolume', 'host1:brick'),
|
||||
'Error: Brick paths must start with /')
|
||||
self.assertRaises(
|
||||
SaltInvocationError, glusterfs.create, 'newvolume', 'host1:brick')
|
||||
|
||||
mock = MagicMock(return_value='')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.create('newvolume', 'host1/brick'),
|
||||
'Error: Brick syntax is <peer>:<path>')
|
||||
self.assertRaises(
|
||||
SaltInvocationError, glusterfs.create, 'newvolume', 'host1/brick')
|
||||
|
||||
mock = MagicMock(return_value='creation success')
|
||||
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')
|
||||
|
||||
mock = MagicMock(return_value='')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.create('newvolume', 'host1:/brick',
|
||||
True, True, True,
|
||||
'tcp', True), '')
|
||||
|
||||
mock = MagicMock(return_value='')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.create('newvolume', 'host1:/brick'),
|
||||
'Volume newvolume created. Start volume to use')
|
||||
|
@ -96,11 +235,11 @@ class GlusterfsTestCase(TestCase):
|
|||
'''
|
||||
Test if it list configured volumes
|
||||
'''
|
||||
mock = MagicMock(return_value='No volumes present in cluster')
|
||||
mock = MagicMock(return_value=xml_volume_absent)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertListEqual(glusterfs.list_volumes(), [])
|
||||
|
||||
mock = MagicMock(return_value='Newvolume1\nNewvolume2')
|
||||
mock = MagicMock(return_value=xml_volume_present)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertListEqual(glusterfs.list_volumes(),
|
||||
['Newvolume1', 'Newvolume2'])
|
||||
|
@ -111,50 +250,98 @@ class GlusterfsTestCase(TestCase):
|
|||
'''
|
||||
Test if it check the status of a gluster volume.
|
||||
'''
|
||||
mock = MagicMock(return_value='No volumes present in cluster')
|
||||
mock = MagicMock(return_value=xml_command_fail)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertDictEqual(glusterfs.status('myvolume'),
|
||||
{'bricks': {}, 'healers': {}, 'nfs': {}})
|
||||
self.assertRaises(
|
||||
CommandExecutionError, glusterfs.status, 'myvol1')
|
||||
|
||||
mock = MagicMock(return_value='does not exist\n')
|
||||
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.assertEqual(glusterfs.status('myvolume'), 'does not exist')
|
||||
|
||||
mock = MagicMock(return_value='is not started')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.status('myvolume'), 'is not started')
|
||||
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={})
|
||||
with patch.object(glusterfs, 'status', mock_status):
|
||||
mock = MagicMock(return_value='creation success')
|
||||
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='')
|
||||
with patch.object(glusterfs, 'status', mock_status):
|
||||
mock_run = MagicMock(return_value='creation success')
|
||||
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='does not exist')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.start_volume('Newvolume1'),
|
||||
'does not exist')
|
||||
|
||||
mock_run = MagicMock(return_value='No volumes present in cluster')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}):
|
||||
self.assertEqual(glusterfs.start_volume('mycluster'),
|
||||
'Volume does not exist')
|
||||
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
|
||||
|
||||
|
@ -164,19 +351,20 @@ class GlusterfsTestCase(TestCase):
|
|||
'''
|
||||
mock = MagicMock(return_value={})
|
||||
with patch.object(glusterfs, 'status', mock):
|
||||
mock = MagicMock(return_value='creation success')
|
||||
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='No volume exist')
|
||||
mock = MagicMock(return_value=xml_command_fail)
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.stop_volume('Newvolume1'),
|
||||
'No volume exist')
|
||||
self.assertRaises(
|
||||
CommandExecutionError, glusterfs.stop_volume, 'Newvolume1')
|
||||
|
||||
mock = MagicMock(return_value='')
|
||||
with patch.object(glusterfs, 'status', mock):
|
||||
self.assertEqual(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
|
||||
|
||||
|
@ -184,64 +372,67 @@ class GlusterfsTestCase(TestCase):
|
|||
'''
|
||||
Test if it deletes a gluster volume.
|
||||
'''
|
||||
ret = 'Error: Volume must be stopped before deletion'
|
||||
mock = MagicMock(return_value=['Newvolume1', 'Newvolume2'])
|
||||
with patch.object(glusterfs, 'list_volumes', mock):
|
||||
self.assertEqual(glusterfs.delete('Newvolume3'),
|
||||
'Volume does not exist')
|
||||
# volume doesn't exist
|
||||
self.assertRaises(
|
||||
SaltInvocationError, glusterfs.delete, 'Newvolume3')
|
||||
|
||||
mock = MagicMock(return_value='creation success')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.delete('Newvolume1', False), ret)
|
||||
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)
|
||||
|
||||
mock = MagicMock(return_value='creation success')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.delete('Newvolume1'),
|
||||
'Volume Newvolume1 stopped and deleted')
|
||||
# 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')
|
||||
|
||||
mock = MagicMock(return_value='')
|
||||
with patch.object(glusterfs, 'status', mock):
|
||||
mock = MagicMock(return_value='creation success')
|
||||
# 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')
|
||||
|
||||
mock = MagicMock(return_value='does not exist')
|
||||
with patch.dict(glusterfs.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(glusterfs.delete('Newvolume1'),
|
||||
'does not exist')
|
||||
|
||||
# 'add_volume_bricks' function tests: 1
|
||||
|
||||
def test_add_volume_bricks(self):
|
||||
'''
|
||||
Test if it add brick(s) to an existing volume
|
||||
'''
|
||||
mock = MagicMock(return_value='does not exist')
|
||||
with patch.object(glusterfs, 'status', mock):
|
||||
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1',
|
||||
['bricks']),
|
||||
'does not exist')
|
||||
|
||||
mock = MagicMock(return_value='is not started')
|
||||
with patch.object(glusterfs, 'status', mock):
|
||||
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1',
|
||||
['bricks']),
|
||||
'is not started')
|
||||
# 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'
|
||||
mock = MagicMock(return_value={'bricks': {}, 'healers': {}, 'nfs': {}})
|
||||
with patch.object(glusterfs, 'status', mock):
|
||||
mock = MagicMock(return_value='creation success')
|
||||
# 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='')
|
||||
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}):
|
||||
self.assertEqual(glusterfs.add_volume_bricks('Newvolume1',
|
||||
['bricks']), '')
|
||||
|
||||
# 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')
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue