Mini Shell
"""
Manage Perl modules using CPAN
.. versionadded:: 2015.5.0
"""
import logging
import os
import os.path
import salt.utils.files
import salt.utils.path
log = logging.getLogger(__name__)
# Don't shadow built-ins.
__func_alias__ = {"list_": "list"}
def __virtual__():
"""
Only work on supported POSIX-like systems
"""
if salt.utils.path.which("cpan"):
return True
return (False, "Unable to locate cpan. Make sure it is installed and in the PATH.")
def install(module):
"""
Install a Perl module from CPAN
CLI Example:
.. code-block:: bash
salt '*' cpan.install Template::Alloy
"""
ret = {
"old": None,
"new": None,
}
old_info = show(module)
cmd = f"cpan -i {module}"
out = __salt__["cmd.run"](cmd)
if "don't know what it is" in out:
ret["error"] = "CPAN cannot identify this package"
return ret
new_info = show(module)
ret["old"] = old_info.get("installed version", None)
ret["new"] = new_info["installed version"]
return ret
def remove(module, details=False):
"""
Attempt to remove a Perl module that was installed from CPAN. Because the
``cpan`` command doesn't actually support "uninstall"-like functionality,
this function will attempt to do what it can, with what it has from CPAN.
Until this function is declared stable, USE AT YOUR OWN RISK!
CLI Example:
.. code-block:: bash
salt '*' cpan.remove Old::Package
"""
ret = {
"old": None,
"new": None,
}
info = show(module)
if "error" in info:
return {"error": info["error"]}
version = info.get("installed version", None)
if version is None:
return ret
ret["old"] = version
if "cpan build dirs" not in info:
return {"error": "No CPAN data available to use for uninstalling"}
mod_pathfile = module.replace("::", "/") + ".pm"
ins_path = info["installed file"].replace(mod_pathfile, "")
files = []
for build_dir in info["cpan build dirs"]:
contents = os.listdir(build_dir)
if "MANIFEST" not in contents:
continue
mfile = os.path.join(build_dir, "MANIFEST")
with salt.utils.files.fopen(mfile, "r") as fh_:
for line in fh_.readlines():
line = salt.utils.stringutils.to_unicode(line)
if line.startswith("lib/"):
files.append(line.replace("lib/", ins_path).strip())
rm_details = {}
for file_ in files:
if file_ in rm_details:
continue
log.trace("Removing %s", file_)
if __salt__["file.remove"](file_):
rm_details[file_] = "removed"
else:
rm_details[file_] = "unable to remove"
if details:
ret["details"] = rm_details
return ret
def list_():
"""
List installed Perl modules, and the version installed
CLI Example:
.. code-block:: bash
salt '*' cpan.list
"""
ret = {}
cmd = "cpan -l"
out = __salt__["cmd.run"](cmd).splitlines()
for line in out:
comps = line.split()
ret[comps[0]] = comps[1]
return ret
def show(module):
"""
Show information about a specific Perl module
CLI Example:
.. code-block:: bash
salt '*' cpan.show Template::Alloy
"""
ret = {}
ret["name"] = module
# This section parses out details from CPAN, if possible
cmd = f"cpan -D {module}"
out = __salt__["cmd.run"](cmd).splitlines()
mode = "skip"
info = []
for line in out:
if line.startswith("-------------"):
mode = "parse"
continue
if mode == "skip":
continue
info.append(line)
if len(info) == 6:
# If the module is not installed, we'll be short a line
info.insert(2, "")
if len(info) < 6:
# This must not be a real package
ret["error"] = "This package does not seem to exist"
return ret
ret["description"] = info[0].strip()
ret["cpan file"] = info[1].strip()
if info[2].strip():
ret["installed file"] = info[2].strip()
else:
ret["installed file"] = None
comps = info[3].split(":")
if len(comps) > 1:
ret["installed version"] = comps[1].strip()
if "installed version" not in ret or not ret["installed version"]:
ret["installed version"] = None
comps = info[4].split(":")
comps = comps[1].split()
ret["cpan version"] = comps[0].strip()
ret["author name"] = info[5].strip()
ret["author email"] = info[6].strip()
# Check and see if there are cpan build directories
config = show_config()
build_dir = config.get("build_dir", None)
if build_dir is not None:
ret["cpan build dirs"] = []
builds = os.listdir(build_dir)
pfile = module.replace("::", "-")
for file_ in builds:
if file_.startswith(pfile):
ret["cpan build dirs"].append(os.path.join(build_dir, file_))
return ret
def show_config():
"""
Return a dict of CPAN configuration values
CLI Example:
.. code-block:: bash
salt '*' cpan.show_config
"""
ret = {}
cmd = "cpan -J"
out = __salt__["cmd.run"](cmd).splitlines()
for line in out:
if "=>" not in line:
# TODO: Some options take up multiple lines, so this doesn't always work
continue
comps = line.split("=>")
key = comps[0].replace("'", "").strip()
val = comps[1].replace("',", "").replace("'", "").strip()
ret[key] = val
return ret
Zerion Mini Shell 1.0