Mini Shell
"""
Module for running imgadm command on SmartOS
"""
import logging
import salt.utils.json
import salt.utils.path
import salt.utils.platform
log = logging.getLogger(__name__)
# Function aliases
__func_alias__ = {
"list_installed": "list",
"update_installed": "update",
"import_image": "import",
}
# Define the module's virtual name
__virtualname__ = "imgadm"
def __virtual__():
"""
Provides imgadm only on SmartOS
"""
if salt.utils.platform.is_smartos_globalzone() and salt.utils.path.which("imgadm"):
return __virtualname__
return (
False,
f"{__virtualname__} module can only be loaded on SmartOS compute nodes",
)
def _exit_status(retcode, stderr=None):
"""
Translate exit status of imgadm
"""
ret = {
0: "Successful completion.",
1: "An error occurred." if not stderr else stderr,
2: "Usage error.",
3: "Image not installed.",
}
return ret[retcode]
def _parse_image_meta(image=None, detail=False):
ret = None
if image and "Error" in image:
ret = image
elif image and "manifest" in image and "name" in image["manifest"]:
name = image["manifest"]["name"]
version = image["manifest"]["version"]
os = image["manifest"]["os"]
description = image["manifest"]["description"]
published = image["manifest"]["published_at"]
source = image["source"]
if image["manifest"]["name"] == "docker-layer":
# NOTE: skip docker-layer unless it has a docker:repo and docker:tag
name = None
docker_repo = None
docker_tag = None
for tag in image["manifest"]["tags"]:
if tag.startswith("docker:tag:") and image["manifest"]["tags"][tag]:
docker_tag = tag.split(":")[-1]
elif tag == "docker:repo":
docker_repo = image["manifest"]["tags"][tag]
if docker_repo and docker_tag:
name = f"{docker_repo}:{docker_tag}"
description = (
"Docker image imported from {repo}:{tag} on {date}.".format(
repo=docker_repo,
tag=docker_tag,
date=published,
)
)
if name and detail:
ret = {
"name": name,
"version": version,
"os": os,
"description": description,
"published": published,
"source": source,
}
elif name:
ret = "{name}@{version} [{published}]".format(
name=name,
version=version,
published=published,
)
else:
log.debug("smartos_image - encountered invalid image payload: %s", image)
ret = {"Error": "This looks like an orphaned image, image payload was invalid."}
return ret
def _split_docker_uuid(uuid):
"""
Split a smartos docker uuid into repo and tag
"""
if uuid:
uuid = uuid.split(":")
if len(uuid) == 2:
tag = uuid[1]
repo = uuid[0]
return repo, tag
return None, None
def _is_uuid(uuid):
"""
Check if uuid is a valid smartos uuid
Example: e69a0918-055d-11e5-8912-e3ceb6df4cf8
"""
if uuid and list(len(x) for x in uuid.split("-")) == [8, 4, 4, 4, 12]:
return True
return False
def _is_docker_uuid(uuid):
"""
Check if uuid is a valid smartos docker uuid
Example plexinc/pms-docker:plexpass
"""
repo, tag = _split_docker_uuid(uuid)
return not (not repo and not tag)
def version():
"""
Return imgadm version
CLI Example:
.. code-block:: bash
salt '*' imgadm.version
"""
ret = {}
cmd = "imgadm --version"
res = __salt__["cmd.run"](cmd).splitlines()
ret = res[0].split()
return ret[-1]
def docker_to_uuid(uuid):
"""
Get the image uuid from an imported docker image
.. versionadded:: 2019.2.0
"""
if _is_uuid(uuid):
return uuid
if _is_docker_uuid(uuid):
images = list_installed(verbose=True)
for image_uuid in images:
if "name" not in images[image_uuid]:
continue
if images[image_uuid]["name"] == uuid:
return image_uuid
return None
def update_installed(uuid=""):
"""
Gather info on unknown image(s) (locally installed)
uuid : string
optional uuid of image
CLI Example:
.. code-block:: bash
salt '*' imgadm.update [uuid]
"""
cmd = f"imgadm update {uuid}".rstrip()
__salt__["cmd.run"](cmd)
return {}
def avail(search=None, verbose=False):
"""
Return a list of available images
search : string
search keyword
verbose : boolean (False)
toggle verbose output
CLI Example:
.. code-block:: bash
salt '*' imgadm.avail [percona]
salt '*' imgadm.avail verbose=True
"""
ret = {}
cmd = "imgadm avail -j"
res = __salt__["cmd.run_all"](cmd)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode)
return ret
for image in salt.utils.json.loads(res["stdout"]):
if image["manifest"]["disabled"] or not image["manifest"]["public"]:
continue
if search and search not in image["manifest"]["name"]:
# we skip if we are searching but don't have a match
continue
uuid = image["manifest"]["uuid"]
data = _parse_image_meta(image, verbose)
if data:
ret[uuid] = data
return ret
def list_installed(verbose=False):
"""
Return a list of installed images
verbose : boolean (False)
toggle verbose output
.. versionchanged:: 2019.2.0
Docker images are now also listed
CLI Example:
.. code-block:: bash
salt '*' imgadm.list
salt '*' imgadm.list docker=True
salt '*' imgadm.list verbose=True
"""
ret = {}
cmd = "imgadm list -j"
res = __salt__["cmd.run_all"](cmd)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode)
return ret
for image in salt.utils.json.loads(res["stdout"]):
uuid = image["manifest"]["uuid"]
data = _parse_image_meta(image, verbose)
if data:
ret[uuid] = data
return ret
def show(uuid):
"""
Show manifest of a given image
uuid : string
uuid of image
CLI Example:
.. code-block:: bash
salt '*' imgadm.show e42f8c84-bbea-11e2-b920-078fab2aab1f
salt '*' imgadm.show plexinc/pms-docker:plexpass
"""
ret = {}
if _is_uuid(uuid) or _is_docker_uuid(uuid):
cmd = f"imgadm show {uuid}"
res = __salt__["cmd.run_all"](cmd, python_shell=False)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode, res["stderr"])
else:
ret = salt.utils.json.loads(res["stdout"])
else:
ret["Error"] = f"{uuid} is not a valid uuid."
return ret
def get(uuid):
"""
Return info on an installed image
uuid : string
uuid of image
CLI Example:
.. code-block:: bash
salt '*' imgadm.get e42f8c84-bbea-11e2-b920-078fab2aab1f
salt '*' imgadm.get plexinc/pms-docker:plexpass
"""
ret = {}
if _is_docker_uuid(uuid):
uuid = docker_to_uuid(uuid)
if _is_uuid(uuid):
cmd = f"imgadm get {uuid}"
res = __salt__["cmd.run_all"](cmd, python_shell=False)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode, res["stderr"])
else:
ret = salt.utils.json.loads(res["stdout"])
else:
ret["Error"] = f"{uuid} is not a valid uuid."
return ret
def import_image(uuid, verbose=False):
"""
Import an image from the repository
uuid : string
uuid to import
verbose : boolean (False)
toggle verbose output
CLI Example:
.. code-block:: bash
salt '*' imgadm.import e42f8c84-bbea-11e2-b920-078fab2aab1f [verbose=True]
"""
ret = {}
cmd = f"imgadm import {uuid}"
res = __salt__["cmd.run_all"](cmd, python_shell=False)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode)
return ret
uuid = docker_to_uuid(uuid)
data = _parse_image_meta(get(uuid), verbose)
return {uuid: data}
def delete(uuid):
"""
Remove an installed image
uuid : string
Specifies uuid to import
CLI Example:
.. code-block:: bash
salt '*' imgadm.delete e42f8c84-bbea-11e2-b920-078fab2aab1f
"""
ret = {}
cmd = f"imgadm delete {uuid}"
res = __salt__["cmd.run_all"](cmd, python_shell=False)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode)
return ret
# output: Deleted image d5b3865c-0804-11e5-be21-dbc4ce844ddc
result = []
for image in res["stdout"].splitlines():
image = [var for var in image.split(" ") if var]
result.append(image[2])
return result
def vacuum(verbose=False):
"""
Remove unused images
verbose : boolean (False)
toggle verbose output
CLI Example:
.. code-block:: bash
salt '*' imgadm.vacuum [verbose=True]
"""
ret = {}
cmd = "imgadm vacuum -f"
res = __salt__["cmd.run_all"](cmd)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode)
return ret
# output: Deleted image d5b3865c-0804-11e5-be21-dbc4ce844ddc (lx-centos-6@20150601)
result = {}
for image in res["stdout"].splitlines():
image = [var for var in image.split(" ") if var]
result[image[2]] = {
"name": image[3][1 : image[3].index("@")],
"version": image[3][image[3].index("@") + 1 : -1],
}
if verbose:
return result
else:
return list(result.keys())
def sources(verbose=False):
"""
Return a list of available sources
verbose : boolean (False)
toggle verbose output
.. versionadded:: 2019.2.0
CLI Example:
.. code-block:: bash
salt '*' imgadm.sources
"""
ret = {}
cmd = "imgadm sources -j"
res = __salt__["cmd.run_all"](cmd)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode)
return ret
for src in salt.utils.json.loads(res["stdout"]):
ret[src["url"]] = src
del src["url"]
if not verbose:
ret = list(ret)
return ret
def source_delete(source):
"""
Delete a source
source : string
source url to delete
.. versionadded:: 2019.2.0
CLI Example:
.. code-block:: bash
salt '*' imgadm.source_delete https://updates.joyent.com
"""
ret = {}
cmd = f"imgadm sources -d {source}"
res = __salt__["cmd.run_all"](cmd)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode, res["stderr"])
return ret
return sources(False)
def source_add(source, source_type="imgapi"):
"""
Add a new source
source : string
source url to add
source_trype : string (imgapi)
source type, either imgapi or docker
.. versionadded:: 2019.2.0
CLI Example:
.. code-block:: bash
salt '*' imgadm.source_add https://updates.joyent.com
salt '*' imgadm.source_add https://docker.io docker
"""
ret = {}
# NOTE: there are some undocumented deprecated source types
# so we just warn instead of error on those
if source_type not in ["imgapi", "docker"]:
log.warning("Possible unsupported imgage source type specified!")
cmd = f"imgadm sources -a {source} -t {source_type}"
res = __salt__["cmd.run_all"](cmd)
retcode = res["retcode"]
if retcode != 0:
ret["Error"] = _exit_status(retcode, res["stderr"])
return ret
return sources(False)
Zerion Mini Shell 1.0