salt/states/zone.py
# -*- coding: utf-8 -*-
'''
Management of Solaris Zones
:maintainer: Jorge Schrauwen <sjorge@blackdot.be>
:maturity: new
:depends: salt.modules.zoneadm, salt.modules.zonecfg
:platform: solaris
.. versionadded:: 2017.7.0
Below are some examples of how to use this state.
Lets start with creating a zone and installing it.
.. code-block:: yaml
omipkg1_configuration:
zone.present:
- name: omipkg1
- brand: ipkg
- zonepath: /zones/omipkg1
- properties:
- autoboot: true
- ip-type: exclusive
- cpu-shares: 50
- resources:
- attr:
- name: owner
- value: Jorge Schrauwen
- type: string
- attr:
- name: description
- value: OmniOS ipkg zone for testing
- type: string
- capped-memory:
- physical: 64M
omipkg1_installation:
zone.installed:
- name: omipkg1
- require:
- zone: omipkg1_configuration
omipkg1_running:
zone.booted:
- name: omipkg1
- require:
- zone: omipkg1_installation
A zone without network access is not very useful. We could update
the zone.present state in the example above to add a network interface
or we could use a separate state for this.
.. code-block:: yaml
omipkg1_network:
zone.resource_present:
- name: omipkg1
- resource_type: net
- resource_selector_property: mac-addr
- resource_selector_value: "02:08:20:a2:a3:10"
- physical: znic1
- require:
- zone: omipkg1_configuration
Since this is a single tenant system having the owner attribute is pointless.
Let's remove that attribute.
.. note::
The following state run the omipkg1_configuration state will add it again!
If the entire configuration is managed it would be better to add resource_prune
and optionally the resource_selector_property properties to the resource.
.. code-block:: yaml
omipkg1_strip_owner:
zone.resource_present:
- name: omipkg1
- resource_type: attr
- resource_selector_property: name
- resource_selector_value: owner
- require:
- zone: omipkg1_configuration
Let's bump the zone's CPU shares a bit.
.. note::
The following state run the omipkg1_configuration state will set it to 50 again.
Update the entire zone configuration is managed you should update it there instead.
.. code-block:: yaml
omipkg1_more_cpu:
zone.property_present:
- name: omipkg1
- property: cpu-shares
- value: 100
Or we can remove the limit altogether!
.. note::
The following state run the omipkg1_configuration state will set it to 50 again.
Update the entire zone configuration is managed you should set the
property to None (nothing after the :) instead.
.. code-block:: yaml
omipkg1_no_cpu:
zone.property_absent:
- name: omipkg1
- property: cpu-shares
'''
from __future__ import absolute_import, print_function, unicode_literals
# Import Python libs
import logging
# Import Salt libs
import salt.utils.args
import salt.utils.atomicfile
import salt.utils.files
from salt.modules.zonecfg import _parse_value, _zonecfg_resource_default_selectors
from salt.exceptions import CommandExecutionError
from salt.utils.odict import OrderedDict
from salt.utils.dictupdate import merge as merge_dict
log = logging.getLogger(__name__)
__func_alias__ = {
'import_': 'import',
}
# Define the state's virtual name
__virtualname__ = 'zone'
def __virtual__():
'''
Provides zone state on Solaris
'''
if 'zonecfg.create' in __salt__ and 'zoneadm.install' in __salt__:
return True
else:
return (
False,
'{0} state module can only be loaded on Solaris platforms'.format(
__virtualname__
)
)
def property_present(name, property, value):
'''
Ensure property has a certain value
name : string
name of the zone
property : string
name of property
value : string
value of property
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
## sanitize input
value = _parse_value(value)
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
## zone exists
zonecfg = __salt__['zonecfg.info'](name, show_all=True)
if property not in zonecfg or zonecfg[property] != _parse_value(value):
if __opts__['test']:
ret['result'] = True
else:
# update property
zonecfg_res = __salt__['zonecfg.set_property'](name, property, value)
ret['result'] = zonecfg_res['status']
if 'messages' in zonecfg_res:
ret['comment'] = zonecfg_res['message']
if ret['result']:
ret['changes'][property] = _parse_value(value)
if ret['comment'] == '':
ret['comment'] = 'The property {0} is was updated to {1}.'.format(property, value)
elif ret['comment'] == '':
if ret['comment'] == '':
ret['comment'] = 'The property {0} is was not updated to {1}!'.format(property, value)
else:
ret['result'] = True
ret['comment'] = 'The property {0} is already set to {1}.'.format(property, value)
else:
## zone does not exist
ret['result'] = False
ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name)
return ret
def property_absent(name, property):
'''
Ensure property is absent
name : string
name of the zone
property : string
name of property
.. note::
This does a zoneacfg clear call. So the property may be reset to a default value!
Does has the side effect of always having to be called.
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
## zone exists
zonecfg = __salt__['zonecfg.info'](name, show_all=True)
if property in zonecfg:
if __opts__['test']:
ret['result'] = True
else:
# clear property
zonecfg_res = __salt__['zonecfg.clear_property'](name, property)
zonecfg_new = __salt__['zonecfg.info'](name, show_all=True)
ret['result'] = zonecfg_res['status']
if 'messages' in zonecfg_res:
ret['comment'] = zonecfg_res['message']
if ret['result']:
if property not in zonecfg_new:
ret['changes'][property] = None
elif zonecfg[property] != zonecfg_new[property]:
ret['changes'][property] = zonecfg_new[property]
if ret['comment'] == '':
ret['comment'] = 'The property {0} was cleared!'.format(property)
elif ret['comment'] == '':
if ret['comment'] == '':
ret['comment'] = 'The property {0} did not get cleared!'.format(property)
else:
ret['result'] = True
ret['comment'] = 'The property {0} does not exist!'.format(property)
else:
## zone does not exist
ret['result'] = False
ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name)
return ret
def resource_present(name, resource_type, resource_selector_property, resource_selector_value, **kwargs):
'''
Ensure resource exists with provided properties
name : string
name of the zone
resource_type : string
type of resource
resource_selector_property : string
unique resource identifier
resource_selector_value : string
value for resource selection
kwargs : string|int|...
resource properties
.. warning::
Both resource_selector_property and resource_selector_value must be
provided, some properties like ``name`` are already reserved by salt in
states.
.. note::
You can set both resource_selector_property and resource_selector_value
to None for resources that do not require them.
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
# sanitize input
kwargs = salt.utils.args.clean_kwargs(**kwargs)
resource_selector_value = _parse_value(resource_selector_value)
for k, v in kwargs.items():
kwargs[k] = _parse_value(kwargs[k])
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
## zone exists
zonecfg = __salt__['zonecfg.info'](name, show_all=True)
## update kwargs
zonecfg_kwargs = {}
zonecfg_kwargs.update(kwargs)
zonecfg_kwargs['zone'] = name
zonecfg_kwargs['resource_type'] = resource_type
zonecfg_kwargs['resource_selector'] = resource_selector_property
if resource_selector_property:
zonecfg_kwargs[resource_selector_property] = resource_selector_value
## check update or add
if resource_type in zonecfg:
for resource in zonecfg[resource_type]:
if not resource_selector_property or resource[resource_selector_property] == resource_selector_value:
ret['result'] = True
if resource_selector_property:
ret['comment'] = 'the {0} resource {1} is up to date.'.format(
resource_type,
resource_selector_value,
)
else:
ret['comment'] = 'the {0} resource is up to date.'.format(
resource_type,
)
## check if update reauired
for key in kwargs:
log.debug('zone.resource_preent - key=%s value=%s current_value=%s',
key,
resource[key] if key in resource else None,
_parse_value(kwargs[key]),
)
# note: something odd with ncpus property, we fix it here for now
if key == 'ncpus' and key in kwargs:
kwargs[key] = '{0:.2f}'.format(float(kwargs[key]))
if key not in resource:
ret['result'] = None
elif resource[key] != _parse_value(kwargs[key]):
ret['result'] = None
## do update
if ret['result'] is None:
if __opts__['test']:
ret['result'] = True
else:
## update resource
zonecfg_res = __salt__['zonecfg.update_resource'](**zonecfg_kwargs)
ret['result'] = zonecfg_res['status']
if 'message' in zonecfg_res:
ret['comment'] = zonecfg_res['message']
if ret['result']:
ret['changes'][resource_type] = {}
if resource_selector_property:
ret['changes'][resource_type][resource_selector_value] = {}
for key in kwargs if ret['result'] else []:
if resource_selector_property:
ret['changes'][resource_type][resource_selector_value][key] = _parse_value(kwargs[key])
else:
ret['changes'][resource_type][key] = _parse_value(kwargs[key])
if ret['comment'] == '':
if resource_selector_property:
ret['comment'] = 'The {0} resource {1} was updated.'.format(
resource_type,
resource_selector_value,
)
else:
ret['comment'] = 'The {0} resource was updated.'.format(
resource_type,
)
elif ret['comment'] == '':
if resource_selector_property:
ret['comment'] = 'The {0} resource {1} was not updated.'.format(
resource_type,
resource_selector_value,
)
else:
ret['comment'] = 'The {0} resource was not updated.'.format(
resource_type,
)
if ret['result'] is None:
## add
if __opts__['test']:
ret['result'] = True
else:
## add resource
if 'resource_selector' in zonecfg_kwargs:
del zonecfg_kwargs['resource_selector']
zonecfg_res = __salt__['zonecfg.add_resource'](**zonecfg_kwargs)
ret['result'] = zonecfg_res['status']
if 'message' in zonecfg_res:
ret['comment'] = zonecfg_res['message']
if ret['result']:
ret['changes'][resource_type] = {}
if resource_selector_property:
ret['changes'][resource_type][resource_selector_value] = {}
for key in kwargs if ret['result'] else []:
if resource_selector_property:
ret['changes'][resource_type][resource_selector_value][key] = _parse_value(kwargs[key])
else:
ret['changes'][resource_type][key] = _parse_value(kwargs[key])
if ret['comment'] == '':
ret['comment'] = 'The {0} resource {1} was added.'.format(
resource_type,
resource_selector_value,
)
elif ret['comment'] == '':
ret['comment'] = 'The {0} resource {1} was not added.'.format(
resource_type,
resource_selector_value,
)
else:
## zone does not exist
ret['result'] = False
ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name)
return ret
def resource_absent(name, resource_type, resource_selector_property, resource_selector_value):
'''
Ensure resource is absent
name : string
name of the zone
resource_type : string
type of resource
resource_selector_property : string
unique resource identifier
resource_selector_value : string
value for resource selection
.. warning::
Both resource_selector_property and resource_selector_value must be provided, some properties
like ```name``` are already reserved by salt in there states.
.. note::
You can set both resource_selector_property and resource_selector_value to None for
resources that do not require them.
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
# sanitize input
if resource_selector_property:
resource_selector_value = _parse_value(resource_selector_value)
else:
resource_selector_value = None
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
## zone exists
zonecfg = __salt__['zonecfg.info'](name, show_all=True)
if resource_type in zonecfg:
for resource in zonecfg[resource_type]:
if __opts__['test']:
ret['result'] = True
elif not resource_selector_property:
zonecfg_res = __salt__['zonecfg.remove_resource'](
zone=name,
resource_type=resource_type,
resource_key=None,
resource_value=None,
)
ret['result'] = zonecfg_res['status']
if zonecfg_res['status']:
ret['changes'][resource_type] = 'removed'
if ret['comment'] == '':
ret['comment'] = 'The {0} resource was removed.'.format(
resource_type,
)
elif 'messages' in zonecfg_res:
ret['comment'] = zonecfg_res['message']
else:
ret['comment'] = 'The {0} resource was not removed.'.format(
resource_type,
)
elif resource[resource_selector_property] == resource_selector_value:
zonecfg_res = __salt__['zonecfg.remove_resource'](
zone=name,
resource_type=resource_type,
resource_key=resource_selector_property,
resource_value=resource_selector_value,
)
ret['result'] = zonecfg_res['status']
if zonecfg_res['status']:
ret['changes'][resource_type] = {}
ret['changes'][resource_type][resource_selector_value] = 'removed'
if ret['comment'] == '':
ret['comment'] = 'The {0} resource {1} was removed.'.format(
resource_type,
resource_selector_value,
)
elif 'messages' in zonecfg_res:
ret['comment'] = zonecfg_res['message']
else:
ret['comment'] = 'The {0} resource {1} was not removed.'.format(
resource_type,
resource_selector_value,
)
# resource already absent
if ret['result'] is None:
ret['result'] = True
ret['comment'] = 'The {0} resource {1} was absent.'.format(
resource_type,
resource_selector_value,
)
else:
## zone does not exist
ret['result'] = False
ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name)
return ret
def booted(name, single=False):
'''
Ensure zone is booted
name : string
name of the zone
single : boolean
boot in single usermode
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True)
if name in zones:
## zone exists
if zones[name]['state'] == 'running':
## zone is running
ret['result'] = True
ret['comment'] = 'Zone {0} already booted'.format(name)
else:
## try and boot the zone
if not __opts__['test']:
zoneadm_res = __salt__['zoneadm.boot'](name, single)
if __opts__['test'] or zoneadm_res['status']:
ret['result'] = True
ret['changes'][name] = 'booted'
ret['comment'] = 'Zone {0} booted'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to boot {0}'.format(name)
else:
## zone does not exist
ret['comment'] = []
ret['comment'].append(
'The zone {0} is not in the installed or booted state.'.format(name)
)
for zone in zones:
if zones[zone]['uuid'] == name:
ret['comment'].append(
'The zone {0} has a uuid of {1}, please use the zone name instead!'.format(
zone,
name,
)
)
ret['result'] = False
ret['comment'] = "\n".join(ret['comment'])
return ret
def halted(name, graceful=True):
'''
Ensure zone is halted
name : string
name of the zone
graceful : boolean
use shutdown instead of halt if true
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True)
if name in zones:
## zone exists
if zones[name]['state'] != 'running':
## zone is not running
ret['result'] = True
ret['comment'] = 'Zone {0} already halted'.format(name)
else:
## try and halt the zone
if not __opts__['test']:
zoneadm_res = __salt__['zoneadm.shutdown'](name) if graceful else __salt__['zoneadm.halt'](name)
if __opts__['test'] or zoneadm_res['status']:
ret['result'] = True
ret['changes'][name] = 'halted'
ret['comment'] = 'Zone {0} halted'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to halt {0}'.format(name)
else:
## zone does not exist
ret['comment'] = []
ret['comment'].append(
'The zone {0} is not in the installed state.'.format(name)
)
for zone in zones:
if zones[zone]['uuid'] == name:
ret['comment'].append(
'The zone {0} has a uuid of {1}, please use the zone name instead!'.format(
zone,
name,
)
)
## note: a non existing zone is not running, we do not consider this a failure
ret['result'] = True
ret['comment'] = "\n".join(ret['comment'])
return ret
def export(name, path, replace=False):
'''
Export a zones configuration
name : string
name of the zone
path : string
path of file to export too.
replace : boolean
replace the file if it exists
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
## zone exists
if __opts__['test']:
## pretend we did the correct thing
ret['result'] = True
ret['comment'] = 'Zone configartion for {0} exported to {1}'.format(
name,
path,
)
ret['changes'][name] = 'exported'
if __salt__['file.file_exists'](path) and not replace:
ret['result'] = False
ret['changes'] = {}
ret['comment'] = 'File {0} exists, zone configuration for {1} not exported.'.format(
path,
name,
)
else:
## export and update file
cfg_tmp = salt.utils.files.mkstemp()
__salt__['zonecfg.export'](name, cfg_tmp)
if not __salt__['file.file_exists'](path):
## move cfg_tmp to path
try:
__salt__['file.move'](cfg_tmp, path)
except CommandExecutionError:
if __salt__['file.file_exists'](cfg_tmp):
__salt__['file.remove'](cfg_tmp)
ret['result'] = False
ret['comment'] = 'Unable to export zone configuration for {0} to {1}!'.format(
name,
path,
)
else:
ret['result'] = True
ret['comment'] = 'Zone configuration for {0} was exported to {1}.'.format(
name,
path,
)
ret['changes'][name] = 'exported'
else:
cfg_diff = __salt__['file.get_diff'](path, cfg_tmp)
if not cfg_diff:
ret['result'] = True
ret['comment'] = 'Zone configuration for {0} was already exported to {1}.'.format(
name,
path
)
if __salt__['file.file_exists'](cfg_tmp):
__salt__['file.remove'](cfg_tmp)
else:
if replace:
try:
__salt__['file.move'](cfg_tmp, path)
except CommandExecutionError:
if __salt__['file.file_exists'](cfg_tmp):
__salt__['file.remove'](cfg_tmp)
ret['result'] = False
ret['comment'] = 'Unable to be re-export zone configuration for {0} to {1}!'.format(
name,
path,
)
else:
ret['result'] = True
ret['comment'] = 'Zone configuration for {0} was re-exported to {1}.'.format(
name,
path,
)
ret['changes'][name] = 'exported'
else:
ret['result'] = False
ret['comment'] = 'Zone configuration for {0} is different from the one exported to {1}!'.format(
name,
path
)
if __salt__['file.file_exists'](cfg_tmp):
__salt__['file.remove'](cfg_tmp)
else:
## zone does not exist
ret['comment'] = []
ret['comment'].append(
'The zone {0} does not exist.'.format(name)
)
for zone in zones:
if zones[zone]['uuid'] == name:
ret['comment'].append(
'The zone {0} has a uuid of {1}, please use the zone name instead!'.format(
name,
path,
)
)
ret['result'] = False
ret['comment'] = "\n".join(ret['comment'])
return ret
def import_(name, path, mode='import', nodataset=False, brand_opts=None):
'''
Import a zones configuration
name : string
name of the zone
path : string
path of the configuration file to import
mode : string
either import, install, or attach
nodataset : boolean
do not create a ZFS file system
brand_opts : boolean
brand specific options to pass
.. note::
The mode argument can be set to ``import``, ``install``, or ``attach``.
``import``: will only import the configuration
``install``: will import and then try to install the zone
``attach``: will import and then try to attach of the zone
.. code-block:: yaml
omipkg1:
zone.import:
- path: /foo/bar/baz
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name not in zones:
if __opts__['test']:
ret['result'] = True
ret['comment'] = 'Zone {0} was imported from {1}.'.format(
name,
path,
)
ret['changes'][name] = 'imported'
else:
if __salt__['file.file_exists'](path):
res_import = __salt__['zonecfg.import'](name, path)
if not res_import['status']:
ret['result'] = False
ret['comment'] = 'Unable to import zone configuration for {0}!'.format(name)
else:
ret['result'] = True
ret['changes'][name] = 'imported'
ret['comment'] = 'Zone {0} was imported from {1}.'.format(
name,
path,
)
if mode.lower() == 'attach':
res_attach = __salt__['zoneadm.attach'](name, False, brand_opts)
ret['result'] = res_attach['status']
if res_attach['status']:
ret['changes'][name] = 'attached'
ret['comment'] = 'Zone {0} was attached from {1}.'.format(
name,
path,
)
else:
ret['comment'] = []
ret['comment'].append('Failed to attach zone {0} from {1}!'.format(
name,
path,
))
if 'message' in res_attach:
ret['comment'].append(res_attach['message'])
ret['comment'] = "\n".join(ret['comment'])
if mode.lower() == 'install':
res_install = __salt__['zoneadm.install'](name, nodataset, brand_opts)
ret['result'] = res_install['status']
if res_install['status']:
ret['changes'][name] = 'installed'
ret['comment'] = 'Zone {0} was installed from {1}.'.format(
name,
path,
)
else:
ret['comment'] = []
ret['comment'].append('Failed to install zone {0} from {1}!'.format(
name,
path,
))
if 'message' in res_install:
ret['comment'].append(res_install['message'])
ret['comment'] = "\n".join(ret['comment'])
else:
ret['result'] = False
ret['comment'] = 'The file {0} does not exists, unable to import!'.format(path)
else:
## zone exist
ret['result'] = True
ret['comment'] = 'Zone {0} already exists, not importing configuration.'.format(name)
return ret
def present(name, brand, zonepath, properties=None, resources=None):
'''
Ensure a zone with certain properties and resources
name : string
name of the zone
brand : string
brand of the zone
zonepath : string
path of the zone
properties : list of key-value pairs
dict of properties
resources : list of key-value pairs
dict of resources
.. note::
If the zone does not exist it will not be installed.
You can use the ```zone.installed``` state for this.
.. note::
Default resource selectors:
- fs: dir
- net: mac-addr
- device: match
- rctl: name
- attr: name
- dataset: name
- admin: user
.. warning::
Properties and resource will not be removed when they
are absent from the state!
For properties, simple set them to ```None```.
For resources, add the ```resource_prune``` property
and set it to ```True```. Also specify the
```resource_selector_property``` if the default is not
the one you want.
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': []}
## sanitize defaults
if not properties:
properties = []
if not resources:
resources = []
properties.append(OrderedDict({"brand": brand}))
properties.append(OrderedDict({"zonepath": zonepath}))
zones = __salt__['zoneadm.list'](installed=True, configured=True)
## test mode only has limited support
if __opts__['test']:
ret['result'] = None
ret['comment'].append('Cannot determine of changes would happen to the zone {0}.'.format(name))
## create zone if needed
if name not in zones:
if __opts__['test']:
## we pretend we created the zone
res_create = {'status': True}
ret['comment'] = []
else:
## create and install
res_create = __salt__['zonecfg.create'](name, brand, zonepath)
if res_create['status']:
ret['result'] = True
ret['changes'][name] = 'created'
ret['comment'].append('The zone {0} was created.'.format(name))
if not __opts__['test']:
ret['result'] = True
if isinstance(properties, list):
for prop in properties:
if not isinstance(prop, OrderedDict) or len(prop) != 1:
log.warning('zone.present - failed to parse property: %s', prop)
continue
for key, value in prop.items():
res = None
if not value:
res = property_absent(name, key)
elif value:
res = property_present(name, key, value)
if res:
ret['result'] = ret['result'] if res['result'] else False
ret['comment'].append(res['comment'])
if res['changes']:
if 'property' not in ret['changes']:
ret['changes']['property'] = {}
ret['changes']['property'] = merge_dict(ret['changes']['property'], res['changes'])
if isinstance(resources, list):
for resource in resources:
if not isinstance(prop, OrderedDict) or len(prop) != 1:
log.warning('zone.present - failed to parse resource: %s', resource)
continue
for key, value in resource.items():
zonecfg = __salt__['zonecfg.info'](name, show_all=True)
resource_cfg = {}
resource_cfg['resource_type'] = key
if isinstance(value, list):
for respv in value:
resource_cfg.update(dict(respv))
resource_prune = False
resource_selector_property = None
if 'resource_prune' in resource_cfg:
resource_prune = resource_cfg['resource_prune']
del resource_cfg['resource_prune']
if 'resource_selector_property' in resource_cfg:
resource_selector_property = resource_cfg['resource_selector_property']
del resource_cfg['resource_selector_property']
if not resource_selector_property and key in _zonecfg_resource_default_selectors:
resource_selector_property = _zonecfg_resource_default_selectors[key]
res = None
if resource_prune:
res = resource_absent(
name,
resource_cfg['resource_type'],
resource_selector_property=resource_selector_property,
resource_selector_value=resource_cfg[resource_selector_property] if resource_selector_property else None,
)
else:
resource_cfg['resource_selector_property'] = resource_selector_property
if resource_selector_property in resource_cfg:
resource_cfg['resource_selector_value'] = resource_cfg[resource_selector_property]
else:
resource_cfg['resource_selector_value'] = None
resource_cfg['name'] = name # we do this last because name can also be a attrib value
res = resource_present(**resource_cfg)
if res:
ret['result'] = ret['result'] if res['result'] else False
ret['comment'].append(res['comment'])
if res['changes']:
if 'resource' not in ret['changes']:
ret['changes']['resource'] = {}
ret['changes']['resource'] = merge_dict(ret['changes']['resource'], res['changes'])
if isinstance(ret['comment'], list):
ret['comment'] = "\n".join(ret['comment'])
return ret
def absent(name, uninstall=False):
'''
Ensure a zone is absent
name : string
name of the zone
uninstall : boolean
when true, uninstall instead of detaching the zone first.
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
if __opts__['test']:
ret['result'] = True
ret['changes'][name] = 'removed'
ret['comment'] = 'Zone {0} was removed.'.format(name)
else:
ret['result'] = True
if uninstall and zones[name]['state'] in ['running', 'installed']:
res_halt = __salt__['zoneadm.halt'](name)
res_uninstall = __salt__['zoneadm.uninstall'](name)
ret['result'] = res_uninstall['status']
if ret['result']:
ret['changes'][name] = 'uninstalled'
ret['comment'] = 'The zone {0} was uninstalled.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to uninstall zone {0}!'.format(name))
if 'message' in res_uninstall:
ret['comment'].append(res_uninstall['message'])
ret['comment'] = "\n".join(ret['comment'])
elif zones[name]['state'] == 'installed':
res_detach = __salt__['zoneadm.detach'](name)
ret['result'] = res_detach['status']
if ret['result']:
ret['changes'][name] = 'detached'
ret['comment'] = 'The zone {0} was detached.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to detach zone {0}!'.format(name))
if 'message' in res_detach:
ret['comment'].append(res_detach['message'])
ret['comment'] = "\n".join(ret['comment'])
if ret['result']:
res_delete = __salt__['zonecfg.delete'](name)
ret['result'] = res_delete['status']
if ret['result']:
ret['changes'][name] = 'deleted'
ret['comment'] = 'The zone {0} was delete.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to delete zone {0}!'.format(name))
if 'message' in res_delete:
ret['comment'].append(res_delete['message'])
ret['comment'] = "\n".join(ret['comment'])
else:
ret['result'] = True
ret['comment'] = 'Zone {0} does not exist.'.format(name)
return ret
def attached(name, force=False):
'''
Ensure zone is attached
name : string
name of the zone
force : boolean
force attach the zone
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
if zones[name]['state'] == 'configured':
if __opts__['test']:
res_attach = {'status': True}
else:
res_attach = __salt__['zoneadm.attach'](name, force)
ret['result'] = res_attach['status']
if ret['result']:
ret['changes'][name] = 'attached'
ret['comment'] = 'The zone {0} was attached.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to attach zone {0}!'.format(name))
if 'message' in res_attach:
ret['comment'].append(res_attach['message'])
ret['comment'] = "\n".join(ret['comment'])
else:
ret['result'] = True
ret['comment'] = 'zone {0} already attached.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'zone {0} is not configured!'.format(name)
return ret
def detached(name):
'''
Ensure zone is detached
name : string
name of the zone
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
if zones[name]['state'] != 'configured':
if __opts__['test']:
res_detach = {'status': True}
else:
res_detach = __salt__['zoneadm.detach'](name)
ret['result'] = res_detach['status']
if ret['result']:
ret['changes'][name] = 'detached'
ret['comment'] = 'The zone {0} was detached.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to detach zone {0}!'.format(name))
if 'message' in res_detach:
ret['comment'].append(res_detach['message'])
ret['comment'] = "\n".join(ret['comment'])
else:
ret['result'] = True
ret['comment'] = 'zone {0} already detached.'.format(name)
else:
## note: a non existing zone is not attached, we do not consider this a failure
ret['result'] = True
ret['comment'] = 'zone {0} is not configured!'.format(name)
return ret
def installed(name, nodataset=False, brand_opts=None):
'''
Ensure zone is installed
name : string
name of the zone
nodataset : boolean
do not create a ZFS file system
brand_opts : boolean
brand specific options to pass
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
if zones[name]['state'] == 'configured':
if __opts__['test']:
res_install = {'status': True}
else:
res_install = __salt__['zoneadm.install'](name, nodataset, brand_opts)
ret['result'] = res_install['status']
if ret['result']:
ret['changes'][name] = 'installed'
ret['comment'] = 'The zone {0} was installed.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to install zone {0}!'.format(name))
if 'message' in res_install:
ret['comment'].append(res_install['message'])
ret['comment'] = "\n".join(ret['comment'])
else:
ret['result'] = True
ret['comment'] = 'zone {0} already installed.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'zone {0} is not configured!'.format(name)
return ret
def uninstalled(name):
'''
Ensure zone is uninstalled
name : string
name of the zone
'''
ret = {'name': name,
'changes': {},
'result': None,
'comment': ''}
zones = __salt__['zoneadm.list'](installed=True, configured=True)
if name in zones:
if zones[name]['state'] != 'configured':
if __opts__['test']:
res_uninstall = {'status': True}
else:
res_uninstall = __salt__['zoneadm.uninstall'](name)
ret['result'] = res_uninstall['status']
if ret['result']:
ret['changes'][name] = 'uninstalled'
ret['comment'] = 'The zone {0} was uninstalled.'.format(name)
else:
ret['comment'] = []
ret['comment'].append('Failed to uninstall zone {0}!'.format(name))
if 'message' in res_uninstall:
ret['comment'].append(res_uninstall['message'])
ret['comment'] = "\n".join(ret['comment'])
else:
ret['result'] = True
ret['comment'] = 'zone {0} already uninstalled.'.format(name)
else:
## note: a non existing zone is not installed, we do not consider this a failure
ret['result'] = True
ret['comment'] = 'zone {0} is not configured!'.format(name)
return ret
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4