mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
implement zpool.present
This commit is contained in:
parent
2c21ee03af
commit
59e99daba9
@ -18,30 +18,28 @@ Management zpool
|
||||
newpool:
|
||||
zpool.present:
|
||||
- config:
|
||||
try_import: false
|
||||
import: false
|
||||
force: true
|
||||
- properties:
|
||||
comment: salty pool
|
||||
comment: salty storage pool
|
||||
- layout:
|
||||
mirror:
|
||||
mirror-0:
|
||||
/dev/disk0
|
||||
/dev/disk1
|
||||
mirror:
|
||||
mirror-1:
|
||||
/dev/disk2
|
||||
/dev/disk3
|
||||
|
||||
.. note::
|
||||
|
||||
only properties will be updated if possible, the layout is fixed at creation time and will not be updated.
|
||||
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Python libs
|
||||
import os
|
||||
import stat
|
||||
import logging
|
||||
|
||||
# Import Salt libs
|
||||
#from salt.utils.odict import OrderedDict
|
||||
from salt.utils.odict import OrderedDict
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -64,9 +62,232 @@ def __virtual__():
|
||||
)
|
||||
|
||||
|
||||
def _check_device(device, config):
|
||||
'''
|
||||
Check if device is present
|
||||
'''
|
||||
if '/' not in device and config['device_dir'] and os.path.exists(config['device_dir']):
|
||||
device = os.path.join(config['device_dir'], device)
|
||||
|
||||
if not os.path.exists(device):
|
||||
return False, 'not present on filesystem'
|
||||
else:
|
||||
mode = os.stat(device).st_mode
|
||||
if not stat.S_ISBLK(mode) and not stat.S_ISREG(mode) and not stat.S_ISCHR(mode):
|
||||
return False, 'not a block device, a file vdev or character special device'
|
||||
|
||||
return True, ''
|
||||
|
||||
|
||||
def present(name, properties=None, filesystem_properties=None, layout=None, config=None):
|
||||
'''
|
||||
ensure storage pool is present on the system
|
||||
|
||||
name : string
|
||||
name of storage pool
|
||||
properties : dict
|
||||
optional set of properties to set for the storage pool
|
||||
filesystem_properties : dict
|
||||
optional set of filesystem properties to set for the storage pool (creation only)
|
||||
layout: dict
|
||||
disk layout to use if the pool does not exist (creation only)
|
||||
config : dict
|
||||
fine grain control over this state
|
||||
|
||||
.. note::
|
||||
|
||||
The following configuration properties can be toggled in the config parameter.
|
||||
- import (true) - try to import the pool before creating it if absent
|
||||
- import_dirs (None) - specify additional locations to scan for devices on import
|
||||
- device_dir (None, SunOS=/dev/rdsk) - specify device directory to use if not absolute path
|
||||
- force (false) - try to force the import or creation
|
||||
|
||||
.. note::
|
||||
|
||||
Because ID's inside the layout dict must be unique they need to have a suffix.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
mirror-0:
|
||||
/tmp/vdisk3
|
||||
/tmp/vdisk2
|
||||
mirror-1:
|
||||
/tmp/vdisk0
|
||||
/tmp/vdisk1
|
||||
|
||||
The above yaml will always result in the following zpool create:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
zpool create mypool mirror /tmp/vdisk3 /tmp/vdisk2 mirror /tmp/vdisk0 /tmp/vdisk1
|
||||
|
||||
Pay attention to the order of your dict!
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
mirror-0:
|
||||
/tmp/vdisk0
|
||||
/tmp/vdisk1
|
||||
/tmp/vdisk2:
|
||||
|
||||
The above will result in the following zpool create:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
zpool create mypool mirror /tmp/vdisk0 /tmp/vdisk1 /tmp/vdisk2
|
||||
|
||||
Creating a 3-way mirror! Why you probably expect it to be mirror root vdev with 2 devices + a root vdev of 1 device!
|
||||
|
||||
'''
|
||||
name = name.lower()
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': None,
|
||||
'comment': ''}
|
||||
|
||||
# config defaults
|
||||
state_config = config if config else {}
|
||||
config = {
|
||||
'import': True,
|
||||
'import_dirs': None,
|
||||
'device_dir': None if __grains__['kernel'] != 'SunOS' else '/dev/rdsk',
|
||||
'force': False
|
||||
}
|
||||
config.update(state_config)
|
||||
log.debug('zpool.present::{0}::config - {1}'.format(name, config))
|
||||
|
||||
# validate layout
|
||||
if layout:
|
||||
layout_valid = True
|
||||
layout_result = {}
|
||||
for root_dev in layout:
|
||||
if '-' in root_dev:
|
||||
if root_dev.split('-')[0] not in ['mirror', 'log', 'cache', 'raidz1', 'raidz2', 'raidz3', 'spare']:
|
||||
layout_valid = False
|
||||
layout_result[root_dev] = 'not a valid vdev type'
|
||||
layout[root_dev] = layout[root_dev].keys() if isinstance(layout[root_dev], OrderedDict) else layout[root_dev].split(' ')
|
||||
|
||||
for dev in layout[root_dev]:
|
||||
dev_info = _check_device(dev, config)
|
||||
if not dev_info[0]:
|
||||
layout_valid = False
|
||||
layout_result[root_dev] = {}
|
||||
layout_result[root_dev][dev] = dev_info[1]
|
||||
else:
|
||||
dev_info = _check_device(root_dev, config)
|
||||
if not dev_info[0]:
|
||||
layout_valid = False
|
||||
layout_result[root_dev] = dev_info[1]
|
||||
|
||||
if not layout_valid:
|
||||
ret['result'] = False
|
||||
ret['comment'] = "{0}".format(layout_result)
|
||||
return ret
|
||||
|
||||
log.debug('zpool.present::{0}::layout - {1}'.format(name, layout))
|
||||
|
||||
# ensure the pool is present
|
||||
ret['result'] = False
|
||||
if __salt__['zpool.exists'](name): # update
|
||||
ret['result'] = True
|
||||
|
||||
# retrieve current properties
|
||||
properties_current = __salt__['zpool.get'](name)[name]
|
||||
|
||||
# figure out if updates needed
|
||||
properties_update = []
|
||||
for prop in properties:
|
||||
if prop not in properties_current:
|
||||
continue
|
||||
|
||||
value = properties[prop]
|
||||
if isinstance(value, bool):
|
||||
value = 'on' if value else 'off'
|
||||
|
||||
if properties_current[prop] != value:
|
||||
properties_update.append(prop)
|
||||
|
||||
# update properties
|
||||
for prop in properties_update:
|
||||
value = properties[prop]
|
||||
res = __salt__['zpool.set'](name, prop, value)
|
||||
|
||||
# also transform value so we match with the return
|
||||
if isinstance(value, bool):
|
||||
value = 'on' if value else 'off'
|
||||
elif ' ' in value:
|
||||
value = "'{0}'".format(value)
|
||||
|
||||
# check return
|
||||
if name in res and prop in res[name] and res[name][prop] == value:
|
||||
if name not in ret['changes']:
|
||||
ret['changes'][name] = {}
|
||||
ret['changes'][name].update(res[name])
|
||||
else:
|
||||
ret['result'] = False
|
||||
if ret['comment'] == '':
|
||||
ret['comment'] = 'The following properties were not updated:'
|
||||
ret['comment'] = '{0} {1}'.format(ret['comment'], prop)
|
||||
|
||||
if ret['result']:
|
||||
ret['comment'] = 'properties updated' if len(ret['changes']) > 0 else 'no update needed'
|
||||
|
||||
else: # import or create
|
||||
if config['import']: # try import
|
||||
log.debug('zpool.present::{0}::importing'.format(name))
|
||||
ret['result'] = __salt__['zpool.import'](
|
||||
name,
|
||||
force=config['force'],
|
||||
dir=config['import_dirs']
|
||||
)
|
||||
ret['result'] = name in ret['result'] and ret['result'][name] == 'imported'
|
||||
if ret['result']:
|
||||
ret['changes'][name] = 'imported'
|
||||
ret['comment'] = 'storage pool {0} was imported'.format(name)
|
||||
|
||||
if not ret['result']: # create
|
||||
if not layout:
|
||||
ret['comment'] = 'storage pool {0} was not imported, no layout specified for creation'.format(name)
|
||||
else:
|
||||
log.debug('zpool.present::{0}::creating'.format(name))
|
||||
if __opts__['test']:
|
||||
ret['result'] = True
|
||||
else:
|
||||
# construct *vdev parameter for zpool.create
|
||||
params = []
|
||||
params.append(name)
|
||||
for root_dev in layout:
|
||||
if '-' in root_dev: # special device
|
||||
params.append(root_dev.split('-')[0]) # add the type by stripping the ID
|
||||
if root_dev.split('-')[0] in ['mirror', 'log', 'cache', 'raidz1', 'raidz2', 'raidz3', 'spare']:
|
||||
for sub_dev in layout[root_dev]: # add all sub devices
|
||||
if '/' not in sub_dev and config['device_dir'] and os.path.exists(config['device_dir']):
|
||||
sub_dev = os.path.join(config['device_dir'], sub_dev)
|
||||
params.append(sub_dev)
|
||||
else: # normal device
|
||||
if '/' not in root_dev and config['device_dir'] and os.path.exists(config['device_dir']):
|
||||
root_dev = os.path.join(config['device_dir'], root_dev)
|
||||
params.append(root_dev)
|
||||
|
||||
# execute zpool.create
|
||||
ret['result'] = __salt__['zpool.create'](*params, force=config['force'], properties=properties, filesystem_properties=filesystem_properties)
|
||||
if name in ret['result'] and ret['result'][name] == 'created':
|
||||
ret['result'] = True
|
||||
else:
|
||||
if name in ret['result']:
|
||||
ret['comment'] = ret['result'][name]
|
||||
ret['result'] = False
|
||||
|
||||
if ret['result']:
|
||||
ret['changes'][name] = 'created'
|
||||
ret['comment'] = 'storage pool {0} was created'.format(name)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name, export=False, force=False):
|
||||
'''
|
||||
Ensure storage pool is not absent on the system
|
||||
ensure storage pool is absent on the system
|
||||
|
||||
name : string
|
||||
name of storage pool
|
||||
@ -82,7 +303,12 @@ def absent(name, export=False, force=False):
|
||||
'result': None,
|
||||
'comment': ''}
|
||||
|
||||
if __salt__['zpool.exists'](name):
|
||||
# config defaults
|
||||
log.debug('zpool.absent::{0}::config::force = {1}'.format(name, force))
|
||||
log.debug('zpool.absent::{0}::config::export = {1}'.format(name, export))
|
||||
|
||||
# ensure the pool is absent
|
||||
if __salt__['zpool.exists'](name): # looks like we need to do some work
|
||||
ret['result'] = False
|
||||
|
||||
if export: # try to export the zpool
|
||||
@ -99,12 +325,13 @@ def absent(name, export=False, force=False):
|
||||
ret['result'] = __salt__['zpool.destroy'](name, force=force)
|
||||
ret['result'] = name in ret['result'] and ret['result'][name] == 'destroyed'
|
||||
|
||||
if ret['result']:
|
||||
if ret['result']: # update the changes and comment
|
||||
ret['changes'][name] = 'exported' if export else 'destroyed'
|
||||
ret['comment'] = 'zpool {0} was {1}'.format(name, ret['changes'][name])
|
||||
else:
|
||||
ret['comment'] = 'storage pool {0} was {1}'.format(name, ret['changes'][name])
|
||||
|
||||
else: # we are looking good
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'zpool {0} is absent'.format(name)
|
||||
ret['comment'] = 'storage pool {0} is absent'.format(name)
|
||||
|
||||
return ret
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user