mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 17:33:54 +00:00
implement zpool.present
This commit is contained in:
parent
2c21ee03af
commit
59e99daba9
@ -18,30 +18,28 @@ Management zpool
|
|||||||
newpool:
|
newpool:
|
||||||
zpool.present:
|
zpool.present:
|
||||||
- config:
|
- config:
|
||||||
try_import: false
|
import: false
|
||||||
force: true
|
force: true
|
||||||
- properties:
|
- properties:
|
||||||
comment: salty pool
|
comment: salty storage pool
|
||||||
- layout:
|
- layout:
|
||||||
mirror:
|
mirror-0:
|
||||||
/dev/disk0
|
/dev/disk0
|
||||||
/dev/disk1
|
/dev/disk1
|
||||||
mirror:
|
mirror-1:
|
||||||
/dev/disk2
|
/dev/disk2
|
||||||
/dev/disk3
|
/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
|
from __future__ import absolute_import
|
||||||
|
|
||||||
# Import Python libs
|
# Import Python libs
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Import Salt libs
|
# Import Salt libs
|
||||||
#from salt.utils.odict import OrderedDict
|
from salt.utils.odict import OrderedDict
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
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):
|
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 : string
|
||||||
name of storage pool
|
name of storage pool
|
||||||
@ -82,7 +303,12 @@ def absent(name, export=False, force=False):
|
|||||||
'result': None,
|
'result': None,
|
||||||
'comment': ''}
|
'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
|
ret['result'] = False
|
||||||
|
|
||||||
if export: # try to export the zpool
|
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'] = __salt__['zpool.destroy'](name, force=force)
|
||||||
ret['result'] = name in ret['result'] and ret['result'][name] == 'destroyed'
|
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['changes'][name] = 'exported' if export else 'destroyed'
|
||||||
ret['comment'] = 'zpool {0} was {1}'.format(name, ret['changes'][name])
|
ret['comment'] = 'storage pool {0} was {1}'.format(name, ret['changes'][name])
|
||||||
else:
|
|
||||||
|
else: # we are looking good
|
||||||
ret['result'] = True
|
ret['result'] = True
|
||||||
ret['comment'] = 'zpool {0} is absent'.format(name)
|
ret['comment'] = 'storage pool {0} is absent'.format(name)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user