Mini Shell
"""
Manage the information in the hosts file
"""
import errno
import logging
import os
import salt.utils.files
import salt.utils.odict as odict
import salt.utils.stringutils
log = logging.getLogger(__name__)
# pylint: disable=C0103
def __get_hosts_filename():
"""
Return the path to the appropriate hosts file
"""
try:
return __context__["hosts.__get_hosts_filename"]
except KeyError:
__context__["hosts.__get_hosts_filename"] = __salt__["config.option"](
"hosts.file"
)
return __context__["hosts.__get_hosts_filename"]
def _get_or_create_hostfile():
"""
Wrapper of __get_hosts_filename but create host file if it
does not exist.
"""
hfn = __get_hosts_filename()
if hfn is None:
hfn = ""
if not os.path.exists(hfn):
with salt.utils.files.fopen(hfn, "w"):
pass
return hfn
def _list_hosts():
"""
Return the hosts found in the hosts file in as an OrderedDict
"""
try:
return __context__["hosts._list_hosts"]
except KeyError:
count = 0
hfn = __get_hosts_filename()
ret = odict.OrderedDict()
try:
with salt.utils.files.fopen(hfn) as ifile:
for line in ifile:
line = salt.utils.stringutils.to_unicode(line).strip()
if not line:
continue
if line.startswith("#"):
ret.setdefault(f"comment-{count}", []).append(line)
count += 1
continue
comment = None
if "#" in line:
comment = line[line.index("#") + 1 :].lstrip()
line = line[: line.index("#")].strip()
comps = line.split()
ip = comps.pop(0)
if comment:
ret.setdefault(ip, {}).setdefault("aliases", []).extend(comps)
ret.setdefault(ip, {}).update({"comment": comment})
else:
ret.setdefault(ip, {}).setdefault("aliases", []).extend(comps)
except OSError as exc:
salt.utils.files.process_read_exception(exc, hfn, ignore=errno.ENOENT)
# Don't set __context__ since we weren't able to read from the
# hosts file.
return ret
__context__["hosts._list_hosts"] = ret
return ret
def list_hosts():
"""
Return the hosts found in the hosts file in this format::
{'<ip addr>': ['alias1', 'alias2', ...]}
CLI Example:
.. code-block:: bash
salt '*' hosts.list_hosts
"""
# msgpack does not like OrderedDict's
return dict(_list_hosts())
def get_ip(host):
"""
Return the ip associated with the named host
CLI Example:
.. code-block:: bash
salt '*' hosts.get_ip <hostname>
"""
hosts = _list_hosts()
if not hosts:
return ""
# Look for the op
for addr in hosts:
if isinstance(hosts[addr], dict) and "aliases" in hosts[addr]:
_hosts = hosts[addr]["aliases"]
if host in _hosts:
return addr
# ip not found
return ""
def get_alias(ip):
"""
Return the list of aliases associated with an ip
Aliases (host names) are returned in the order in which they
appear in the hosts file. If there are no aliases associated with
the IP, an empty list is returned.
CLI Example:
.. code-block:: bash
salt '*' hosts.get_alias <ip addr>
"""
hosts = _list_hosts()
if ip in list(hosts):
return hosts[ip]["aliases"]
return []
def has_pair(ip, alias):
"""
Return true if the alias is set
CLI Example:
.. code-block:: bash
salt '*' hosts.has_pair <ip> <alias>
"""
hosts = _list_hosts()
try:
if isinstance(alias, list):
return set(alias).issubset(hosts[ip]["aliases"])
else:
return alias in hosts[ip]["aliases"]
except KeyError:
return False
def set_host(ip, alias, comment=None):
"""
Set the host entry in the hosts file for the given ip, this will overwrite
any previous entry for the given ip
.. versionchanged:: 2016.3.0
If ``alias`` does not include any host names (it is the empty
string or contains only whitespace), all entries for the given
IP address are removed.
CLI Example:
.. code-block:: bash
salt '*' hosts.set_host <ip> <alias>
"""
hfn = _get_or_create_hostfile()
ovr = False
if not os.path.isfile(hfn):
return False
# Make sure future calls to _list_hosts() will re-read the file
__context__.pop("hosts._list_hosts", None)
if comment:
line_to_add = salt.utils.stringutils.to_bytes(
ip + "\t\t" + alias + "\t\t# " + comment + os.linesep
)
else:
line_to_add = salt.utils.stringutils.to_bytes(ip + "\t\t" + alias + os.linesep)
# support removing a host entry by providing an empty string
if not alias.strip():
line_to_add = b""
with salt.utils.files.fopen(hfn, "rb") as fp_:
lines = fp_.readlines()
for ind, _ in enumerate(lines):
tmpline = lines[ind].strip()
if not tmpline:
continue
if tmpline.startswith(b"#"):
continue
comps = tmpline.split()
if comps[0] == salt.utils.stringutils.to_bytes(ip):
if not ovr:
lines[ind] = line_to_add
ovr = True
else: # remove other entries
lines[ind] = b""
linesep_bytes = salt.utils.stringutils.to_bytes(os.linesep)
if not ovr:
# make sure there is a newline
if lines and not lines[-1].endswith(linesep_bytes):
lines[-1] += linesep_bytes
line = line_to_add
lines.append(line)
with salt.utils.files.fopen(hfn, "wb") as ofile:
ofile.writelines(lines)
return True
def rm_host(ip, alias):
"""
Remove a host entry from the hosts file
CLI Example:
.. code-block:: bash
salt '*' hosts.rm_host <ip> <alias>
"""
if not has_pair(ip, alias):
return True
# Make sure future calls to _list_hosts() will re-read the file
__context__.pop("hosts._list_hosts", None)
hfn = _get_or_create_hostfile()
with salt.utils.files.fopen(hfn, "rb") as fp_:
lines = fp_.readlines()
for ind, _ in enumerate(lines):
tmpline = lines[ind].strip()
if not tmpline:
continue
if tmpline.startswith(b"#"):
continue
comps = tmpline.split()
comment = None
if b"#" in tmpline:
host_info, comment = tmpline.split(b"#")
comment = salt.utils.stringutils.to_bytes(comment).lstrip()
else:
host_info = tmpline
host_info = salt.utils.stringutils.to_bytes(host_info)
comps = host_info.split()
b_ip = salt.utils.stringutils.to_bytes(ip)
b_alias = salt.utils.stringutils.to_bytes(alias)
if comps[0] == b_ip:
newline = comps[0] + b"\t\t"
for existing in comps[1:]:
if existing == b_alias:
continue
newline += existing + b" "
if newline.strip() == b_ip:
# No aliases exist for the line, make it empty
lines[ind] = b""
else:
# Only an alias was removed
if comment:
lines[ind] = (
newline
+ b"# "
+ comment
+ salt.utils.stringutils.to_bytes(os.linesep)
)
else:
lines[ind] = newline + salt.utils.stringutils.to_bytes(os.linesep)
with salt.utils.files.fopen(hfn, "wb") as ofile:
ofile.writelines(lines)
return True
def add_host(ip, alias):
"""
Add a host to an existing entry, if the entry is not in place then create
it with the given host
CLI Example:
.. code-block:: bash
salt '*' hosts.add_host <ip> <alias>
"""
hfn = _get_or_create_hostfile()
if not os.path.isfile(hfn):
return False
if has_pair(ip, alias):
return True
hosts = _list_hosts()
# Make sure future calls to _list_hosts() will re-read the file
__context__.pop("hosts._list_hosts", None)
inserted = False
for i, h in hosts.items():
for num, host in enumerate(h):
if isinstance(h, list):
if host.startswith("#") and i == ip:
h.insert(num, alias)
inserted = True
if not inserted:
hosts.setdefault(ip, {}).setdefault("aliases", []).append(alias)
_write_hosts(hosts)
return True
def set_comment(ip, comment):
"""
Set the comment for a host to an existing entry,
if the entry is not in place then return False
CLI Example:
.. code-block:: bash
salt '*' hosts.set_comment <ip> <comment>
"""
hfn = _get_or_create_hostfile()
if not os.path.isfile(hfn):
return False
hosts = _list_hosts()
# Make sure future calls to _list_hosts() will re-read the file
__context__.pop("hosts._list_hosts", None)
if ip not in hosts:
return False
if "comment" in hosts[ip]:
if comment != hosts[ip]["comment"]:
hosts[ip]["comment"] = comment
_write_hosts(hosts)
else:
return True
else:
hosts[ip]["comment"] = comment
_write_hosts(hosts)
return True
def _write_hosts(hosts):
lines = []
for ip, host_info in hosts.items():
if ip:
if ip.startswith("comment"):
line = "".join(host_info)
else:
if "comment" in host_info:
line = "{}\t\t{}\t\t# {}".format(
ip, " ".join(host_info["aliases"]), host_info["comment"]
)
else:
line = "{}\t\t{}".format(ip, " ".join(host_info["aliases"]))
lines.append(line)
hfn = _get_or_create_hostfile()
with salt.utils.files.fopen(hfn, "w+") as ofile:
for line in lines:
if line.strip():
# /etc/hosts needs to end with a newline so that some utils
# that read it do not break
ofile.write(
salt.utils.stringutils.to_str(line.strip() + str(os.linesep))
)
Zerion Mini Shell 1.0