Mini Shell
"""
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
"""
import logging
import salt.utils.args
import salt.utils.atomicfile
import salt.utils.files
from salt.exceptions import CommandExecutionError
from salt.modules.zonecfg import _parse_value, _zonecfg_resource_default_selectors
from salt.utils.dictupdate import merge as merge_dict
from salt.utils.odict import OrderedDict
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,
"{} 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 {} is was updated to {}.".format(
property, value
)
elif ret["comment"] == "":
if ret["comment"] == "":
ret["comment"] = "The property {} is was not updated to {}!".format(
property, value
)
else:
ret["result"] = True
ret["comment"] = "The property {} is already set to {}.".format(
property, value
)
else:
## zone does not exist
ret["result"] = False
ret["comment"] = (
"The zone {} 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"] = f"The property {property} was cleared!"
elif ret["comment"] == "":
if ret["comment"] == "":
ret["comment"] = "The property {} did not get cleared!".format(
property
)
else:
ret["result"] = True
ret["comment"] = f"The property {property} does not exist!"
else:
## zone does not exist
ret["result"] = False
ret["comment"] = (
"The zone {} 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 {} resource {} is up to date.".format(
resource_type,
resource_selector_value,
)
else:
ret["comment"] = "the {} 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] = f"{float(kwargs[key]):.2f}"
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 {} resource {} was updated.".format(
resource_type,
resource_selector_value,
)
)
else:
ret["comment"] = (
"The {} resource was updated.".format(
resource_type,
)
)
elif ret["comment"] == "":
if resource_selector_property:
ret["comment"] = (
"The {} resource {} was not updated.".format(
resource_type,
resource_selector_value,
)
)
else:
ret["comment"] = (
"The {} 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 {} resource {} was added.".format(
resource_type,
resource_selector_value,
)
elif ret["comment"] == "":
ret["comment"] = "The {} resource {} was not added.".format(
resource_type,
resource_selector_value,
)
else:
## zone does not exist
ret["result"] = False
ret["comment"] = (
"The zone {} 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 {} resource was removed.".format(
resource_type,
)
elif "messages" in zonecfg_res:
ret["comment"] = zonecfg_res["message"]
else:
ret["comment"] = "The {} 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 {} resource {} was removed.".format(
resource_type,
resource_selector_value,
)
elif "messages" in zonecfg_res:
ret["comment"] = zonecfg_res["message"]
else:
ret["comment"] = "The {} resource {} was not removed.".format(
resource_type,
resource_selector_value,
)
# resource already absent
if ret["result"] is None:
ret["result"] = True
ret["comment"] = "The {} resource {} was absent.".format(
resource_type,
resource_selector_value,
)
else:
## zone does not exist
ret["result"] = False
ret["comment"] = (
"The zone {} 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"] = f"Zone {name} already booted"
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"] = f"Zone {name} booted"
else:
ret["result"] = False
ret["comment"] = f"Failed to boot {name}"
else:
## zone does not exist
ret["comment"] = []
ret["comment"].append(
f"The zone {name} is not in the installed or booted state."
)
for zone in zones:
if zones[zone]["uuid"] == name:
ret["comment"].append(
"The zone {} has a uuid of {}, 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"] = f"Zone {name} already halted"
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"] = f"Zone {name} halted"
else:
ret["result"] = False
ret["comment"] = f"Failed to halt {name}"
else:
## zone does not exist
ret["comment"] = []
ret["comment"].append(f"The zone {name} is not in the installed state.")
for zone in zones:
if zones[zone]["uuid"] == name:
ret["comment"].append(
"The zone {} has a uuid of {}, 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 {} exported to {}".format(
name,
path,
)
ret["changes"][name] = "exported"
if __salt__["file.file_exists"](path) and not replace:
ret["result"] = False
ret["changes"] = {}
ret["comment"] = (
"File {} exists, zone configuration for {} 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 {} to {}!".format(
name,
path,
)
)
else:
ret["result"] = True
ret["comment"] = (
"Zone configuration for {} was exported to {}.".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 {} was already exported to {}.".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 {}"
" to {}!".format(
name,
path,
)
)
else:
ret["result"] = True
ret["comment"] = (
"Zone configuration for {} was re-exported to {}.".format(
name,
path,
)
)
ret["changes"][name] = "exported"
else:
ret["result"] = False
ret["comment"] = (
"Zone configuration for {} is different from the one"
" exported to {}!".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(f"The zone {name} does not exist.")
for zone in zones:
if zones[zone]["uuid"] == name:
ret["comment"].append(
"The zone {} has a uuid of {}, 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 {} was imported from {}.".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"] = f"Unable to import zone configuration for {name}!"
else:
ret["result"] = True
ret["changes"][name] = "imported"
ret["comment"] = "Zone {} was imported from {}.".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 {} was attached from {}.".format(
name,
path,
)
else:
ret["comment"] = []
ret["comment"].append(
"Failed to attach zone {} from {}!".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 {} was installed from {}.".format(
name,
path,
)
else:
ret["comment"] = []
ret["comment"].append(
"Failed to install zone {} from {}!".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"] = f"The file {path} does not exists, unable to import!"
else:
## zone exist
ret["result"] = True
ret["comment"] = "Zone {} 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(
f"Cannot determine of changes would happen to the zone {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(f"The zone {name} was created.")
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 len(res["changes"]) > 0:
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 len(res["changes"]) > 0:
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"] = f"Zone {name} was removed."
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"] = f"The zone {name} was uninstalled."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to uninstall zone {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"] = f"The zone {name} was detached."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to detach zone {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"] = f"The zone {name} was delete."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to delete zone {name}!")
if "message" in res_delete:
ret["comment"].append(res_delete["message"])
ret["comment"] = "\n".join(ret["comment"])
else:
ret["result"] = True
ret["comment"] = f"Zone {name} does not exist."
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"] = f"The zone {name} was attached."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to attach zone {name}!")
if "message" in res_attach:
ret["comment"].append(res_attach["message"])
ret["comment"] = "\n".join(ret["comment"])
else:
ret["result"] = True
ret["comment"] = f"zone {name} already attached."
else:
ret["result"] = False
ret["comment"] = f"zone {name} is not configured!"
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"] = f"The zone {name} was detached."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to detach zone {name}!")
if "message" in res_detach:
ret["comment"].append(res_detach["message"])
ret["comment"] = "\n".join(ret["comment"])
else:
ret["result"] = True
ret["comment"] = f"zone {name} already detached."
else:
## note: a non existing zone is not attached, we do not consider this a failure
ret["result"] = True
ret["comment"] = f"zone {name} is not configured!"
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"] = f"The zone {name} was installed."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to install zone {name}!")
if "message" in res_install:
ret["comment"].append(res_install["message"])
ret["comment"] = "\n".join(ret["comment"])
else:
ret["result"] = True
ret["comment"] = f"zone {name} already installed."
else:
ret["result"] = False
ret["comment"] = f"zone {name} is not configured!"
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"] = f"The zone {name} was uninstalled."
else:
ret["comment"] = []
ret["comment"].append(f"Failed to uninstall zone {name}!")
if "message" in res_uninstall:
ret["comment"].append(res_uninstall["message"])
ret["comment"] = "\n".join(ret["comment"])
else:
ret["result"] = True
ret["comment"] = f"zone {name} already uninstalled."
else:
## note: a non existing zone is not installed, we do not consider this a failure
ret["result"] = True
ret["comment"] = f"zone {name} is not configured!"
return ret
Zerion Mini Shell 1.0