Permalink
Cannot retrieve contributors at this time
1848 lines (1564 sloc)
57.8 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
salt/salt/modules/status.py /
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Module for returning various status data about a minion. | |
| These data can be useful for compiling into stats later. | |
| """ | |
| import collections | |
| import copy | |
| import datetime | |
| import fnmatch | |
| import itertools | |
| import logging | |
| import os | |
| import re | |
| import time | |
| import salt.channel | |
| import salt.config | |
| import salt.minion | |
| import salt.utils.event | |
| import salt.utils.files | |
| import salt.utils.network | |
| import salt.utils.path | |
| import salt.utils.platform | |
| import salt.utils.stringutils | |
| from salt.exceptions import CommandExecutionError | |
| log = logging.getLogger(__file__) | |
| __virtualname__ = "status" | |
| # Don't shadow built-in's. | |
| __func_alias__ = {"time_": "time"} | |
| log = logging.getLogger(__name__) | |
| def __virtual__(): | |
| """ | |
| Not all functions supported by Windows | |
| """ | |
| if salt.utils.platform.is_windows(): | |
| return False, "Windows platform is not supported by this module" | |
| return __virtualname__ | |
| def _number(text): | |
| """ | |
| Convert a string to a number. | |
| Returns an integer if the string represents an integer, a floating | |
| point number if the string is a real number, or the string unchanged | |
| otherwise. | |
| """ | |
| if text.isdigit(): | |
| return int(text) | |
| try: | |
| return float(text) | |
| except ValueError: | |
| return text | |
| def _get_boot_time_aix(): | |
| """ | |
| Return the number of seconds since boot time on AIX | |
| t=$(LC_ALL=POSIX ps -o etime= -p 1) | |
| d=0 h=0 | |
| case $t in *-*) d=${t%%-*}; t=${t#*-};; esac | |
| case $t in *:*:*) h=${t%%:*}; t=${t#*:};; esac | |
| s=$((d*86400 + h*3600 + ${t%%:*}*60 + ${t#*:})) | |
| """ | |
| res = __salt__["cmd.run_all"]("ps -o etime= -p 1") | |
| if res["retcode"] > 0: | |
| raise CommandExecutionError("Unable to find boot_time for pid 1.") | |
| bt_time = res["stdout"] | |
| match = re.match(r"\s*(?:(\d+)-)?(?:(\d\d):)?(\d\d):(\d\d)\s*", bt_time) | |
| if not match: | |
| raise CommandExecutionError("Unexpected time format.") | |
| groups = match.groups(default="00") | |
| boot_secs = ( | |
| _number(groups[0]) * 86400 | |
| + _number(groups[1]) * 3600 | |
| + _number(groups[2]) * 60 | |
| + _number(groups[3]) | |
| ) | |
| return boot_secs | |
| def _aix_loadavg(): | |
| """ | |
| Return the load average on AIX | |
| """ | |
| # 03:42PM up 9 days, 20:41, 2 users, load average: 0.28, 0.47, 0.69 | |
| uptime = __salt__["cmd.run"]("uptime") | |
| ldavg = uptime.split("load average") | |
| load_avg = ldavg[1].split() | |
| return { | |
| "1-min": load_avg[1].strip(","), | |
| "5-min": load_avg[2].strip(","), | |
| "15-min": load_avg[3], | |
| } | |
| def _aix_nproc(): | |
| """ | |
| Return the maximun number of PROCESSES allowed per user on AIX | |
| """ | |
| nprocs = __salt__["cmd.run"]( | |
| "lsattr -E -l sys0 | grep maxuproc", python_shell=True | |
| ).split() | |
| return _number(nprocs[1]) | |
| def procs(): | |
| """ | |
| Return the process data | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.procs | |
| """ | |
| # Get the user, pid and cmd | |
| ret = {} | |
| uind = 0 | |
| pind = 0 | |
| cind = 0 | |
| plines = __salt__["cmd.run"](__grains__["ps"], python_shell=True).splitlines() | |
| guide = plines.pop(0).split() | |
| if "USER" in guide: | |
| uind = guide.index("USER") | |
| elif "UID" in guide: | |
| uind = guide.index("UID") | |
| if "PID" in guide: | |
| pind = guide.index("PID") | |
| if "COMMAND" in guide: | |
| cind = guide.index("COMMAND") | |
| elif "CMD" in guide: | |
| cind = guide.index("CMD") | |
| for line in plines: | |
| if not line: | |
| continue | |
| comps = line.split() | |
| ret[comps[pind]] = {"user": comps[uind], "cmd": " ".join(comps[cind:])} | |
| return ret | |
| def custom(): | |
| """ | |
| Return a custom composite of status data and info for this minion, | |
| based on the minion config file. An example config like might be:: | |
| status.cpustats.custom: [ 'cpu', 'ctxt', 'btime', 'processes' ] | |
| Where status refers to status.py, cpustats is the function | |
| where we get our data, and custom is this function It is followed | |
| by a list of keys that we want returned. | |
| This function is meant to replace all_status(), which returns | |
| anything and everything, which we probably don't want. | |
| By default, nothing is returned. Warning: Depending on what you | |
| include, there can be a LOT here! | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.custom | |
| """ | |
| ret = {} | |
| conf = __salt__["config.dot_vals"]("status") | |
| for key, val in conf.items(): | |
| func = ".".join(key.split(".")[:-1]) | |
| vals = {} | |
| if func != "status.custom": | |
| try: | |
| vals = __salt__[func]() | |
| for item in val: | |
| try: | |
| ret[item] = vals[item] | |
| except KeyError: | |
| log.warning(f"val {item} not in return of {func}") | |
| ret[item] = "UNKNOWN" | |
| except KeyError: | |
| log.warning(f"custom status {func} isn't loaded") | |
| return ret | |
| def uptime(): | |
| """ | |
| Return the uptime for this system. | |
| .. versionchanged:: 2015.8.9 | |
| The uptime function was changed to return a dictionary of easy-to-read | |
| key/value pairs containing uptime information, instead of the output | |
| from a ``cmd.run`` call. | |
| .. versionchanged:: 2016.11.0 | |
| Support for OpenBSD, FreeBSD, NetBSD, MacOS, and Solaris | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.uptime | |
| """ | |
| curr_seconds = time.time() | |
| # Get uptime in seconds | |
| if salt.utils.platform.is_linux(): | |
| ut_path = "/proc/uptime" | |
| if not os.path.exists(ut_path): | |
| raise CommandExecutionError( | |
| "File {ut_path} was not found.".format(ut_path=ut_path) | |
| ) | |
| with salt.utils.files.fopen(ut_path) as rfh: | |
| seconds = int(float(rfh.read().split()[0])) | |
| elif salt.utils.platform.is_sunos(): | |
| # note: some flavors/versions report the host uptime inside a zone | |
| # https://support.oracle.com/epmos/faces/BugDisplay?id=15611584 | |
| res = __salt__["cmd.run_all"]("kstat -p unix:0:system_misc:boot_time") | |
| if res["retcode"] > 0: | |
| raise CommandExecutionError("The boot_time kstat was not found.") | |
| seconds = int(curr_seconds - int(res["stdout"].split()[-1])) | |
| elif salt.utils.platform.is_openbsd() or salt.utils.platform.is_netbsd(): | |
| bt_data = __salt__["sysctl.get"]("kern.boottime") | |
| if not bt_data: | |
| raise CommandExecutionError("Cannot find kern.boottime system parameter") | |
| seconds = int(curr_seconds - int(bt_data)) | |
| elif salt.utils.platform.is_freebsd() or salt.utils.platform.is_darwin(): | |
| # format: { sec = 1477761334, usec = 664698 } Sat Oct 29 17:15:34 2016 | |
| bt_data = __salt__["sysctl.get"]("kern.boottime") | |
| if not bt_data: | |
| raise CommandExecutionError("Cannot find kern.boottime system parameter") | |
| data = bt_data.split("{")[-1].split("}")[0].strip().replace(" ", "") | |
| uptime = { | |
| k: int( | |
| v, | |
| ) | |
| for k, v in [p.strip().split("=") for p in data.split(",")] | |
| } | |
| seconds = int(curr_seconds - uptime["sec"]) | |
| elif salt.utils.platform.is_aix(): | |
| seconds = _get_boot_time_aix() | |
| else: | |
| return __salt__["cmd.run"]("uptime") | |
| # Setup datetime and timedelta objects | |
| boot_time = datetime.datetime.utcfromtimestamp(curr_seconds - seconds) | |
| curr_time = datetime.datetime.utcfromtimestamp(curr_seconds) | |
| up_time = curr_time - boot_time | |
| # Construct return information | |
| ut_ret = { | |
| "seconds": seconds, | |
| "since_iso": boot_time.isoformat(), | |
| "since_t": int(curr_seconds - seconds), | |
| "days": up_time.days, | |
| "time": "{}:{}".format(up_time.seconds // 3600, up_time.seconds % 3600 // 60), | |
| } | |
| if salt.utils.path.which("who"): | |
| who_cmd = ( | |
| "who" if salt.utils.platform.is_openbsd() else "who -s" | |
| ) # OpenBSD does not support -s | |
| ut_ret["users"] = len(__salt__["cmd.run"](who_cmd).split(os.linesep)) | |
| return ut_ret | |
| def loadavg(): | |
| """ | |
| Return the load averages for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.loadavg | |
| :raises CommandExecutionError: If the system cannot report loadaverages to Python | |
| """ | |
| if __grains__["kernel"] == "AIX": | |
| return _aix_loadavg() | |
| try: | |
| load_avg = os.getloadavg() | |
| except AttributeError: | |
| # Some UNIX-based operating systems do not have os.getloadavg() | |
| raise salt.exceptions.CommandExecutionError( | |
| "status.loadavag is not available on your platform" | |
| ) | |
| return {"1-min": load_avg[0], "5-min": load_avg[1], "15-min": load_avg[2]} | |
| def cpustats(): | |
| """ | |
| Return the CPU stats for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| .. versionchanged:: 2018.3.0 | |
| Added support for OpenBSD | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.cpustats | |
| """ | |
| def linux_cpustats(): | |
| """ | |
| linux specific implementation of cpustats | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/stat", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split() | |
| if comps[0] == "cpu": | |
| ret[comps[0]] = { | |
| "idle": _number(comps[4]), | |
| "iowait": _number(comps[5]), | |
| "irq": _number(comps[6]), | |
| "nice": _number(comps[2]), | |
| "softirq": _number(comps[7]), | |
| "steal": _number(comps[8]), | |
| "system": _number(comps[3]), | |
| "user": _number(comps[1]), | |
| } | |
| elif comps[0] == "intr": | |
| ret[comps[0]] = { | |
| "total": _number(comps[1]), | |
| "irqs": [_number(x) for x in comps[2:]], | |
| } | |
| elif comps[0] == "softirq": | |
| ret[comps[0]] = { | |
| "total": _number(comps[1]), | |
| "softirqs": [_number(x) for x in comps[2:]], | |
| } | |
| else: | |
| ret[comps[0]] = _number(comps[1]) | |
| return ret | |
| def freebsd_cpustats(): | |
| """ | |
| freebsd specific implementation of cpustats | |
| """ | |
| vmstat = __salt__["cmd.run"]("vmstat -P").splitlines() | |
| vm0 = vmstat[0].split() | |
| cpu0loc = vm0.index("cpu0") | |
| vm1 = vmstat[1].split() | |
| usloc = vm1.index("us") | |
| vm2 = vmstat[2].split() | |
| cpuctr = 0 | |
| ret = {} | |
| for cpu in vm0[cpu0loc:]: | |
| ret[cpu] = { | |
| "us": _number(vm2[usloc + 3 * cpuctr]), | |
| "sy": _number(vm2[usloc + 1 + 3 * cpuctr]), | |
| "id": _number(vm2[usloc + 2 + 3 * cpuctr]), | |
| } | |
| cpuctr += 1 | |
| return ret | |
| def sunos_cpustats(): | |
| """ | |
| sunos specific implementation of cpustats | |
| """ | |
| mpstat = __salt__["cmd.run"]("mpstat 1 2").splitlines() | |
| fields = mpstat[0].split() | |
| ret = {} | |
| for cpu in mpstat: | |
| if cpu.startswith("CPU"): | |
| continue | |
| cpu = cpu.split() | |
| ret[_number(cpu[0])] = {} | |
| for i in range(1, len(fields) - 1): | |
| ret[_number(cpu[0])][fields[i]] = _number(cpu[i]) | |
| return ret | |
| def aix_cpustats(): | |
| """ | |
| AIX specific implementation of cpustats | |
| """ | |
| ret = {} | |
| ret["mpstat"] = [] | |
| procn = None | |
| fields = [] | |
| for line in __salt__["cmd.run"]("mpstat -a").splitlines(): | |
| if not line: | |
| continue | |
| procn = len(ret["mpstat"]) | |
| if line.startswith("System"): | |
| comps = line.split(":") | |
| ret["mpstat"].append({}) | |
| ret["mpstat"][procn]["system"] = {} | |
| cpu_comps = comps[1].split() | |
| for comp in cpu_comps: | |
| cpu_vals = comp.split("=") | |
| ret["mpstat"][procn]["system"][cpu_vals[0]] = cpu_vals[1] | |
| if line.startswith("cpu"): | |
| fields = line.split() | |
| continue | |
| if fields: | |
| cpustat = line.split() | |
| ret[_number(cpustat[0])] = {} | |
| for i in range(1, len(fields) - 1): | |
| ret[_number(cpustat[0])][fields[i]] = _number(cpustat[i]) | |
| return ret | |
| def openbsd_cpustats(): | |
| """ | |
| openbsd specific implementation of cpustats | |
| """ | |
| systat = __salt__["cmd.run"]("systat -s 2 -B cpu").splitlines() | |
| fields = systat[3].split() | |
| ret = {} | |
| for cpu in systat[4:]: | |
| cpu_line = cpu.split() | |
| cpu_idx = cpu_line[0] | |
| ret[cpu_idx] = {} | |
| for idx, field in enumerate(fields[1:]): | |
| ret[cpu_idx][field] = cpu_line[idx + 1] | |
| return ret | |
| # dict that return a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_cpustats, | |
| "FreeBSD": freebsd_cpustats, | |
| "Junos": freebsd_cpustats, | |
| "OpenBSD": openbsd_cpustats, | |
| "SunOS": sunos_cpustats, | |
| "AIX": aix_cpustats, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def meminfo(): | |
| """ | |
| Return the memory info for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| .. versionchanged:: 2018.3.0 | |
| Added support for OpenBSD | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.meminfo | |
| """ | |
| def linux_meminfo(): | |
| """ | |
| linux specific implementation of meminfo | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/meminfo", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split() | |
| comps[0] = comps[0].replace(":", "") | |
| ret[comps[0]] = { | |
| "value": comps[1], | |
| } | |
| if len(comps) > 2: | |
| ret[comps[0]]["unit"] = comps[2] | |
| return ret | |
| def freebsd_meminfo(): | |
| """ | |
| freebsd specific implementation of meminfo | |
| """ | |
| sysctlvm = __salt__["cmd.run"]("sysctl vm").splitlines() | |
| sysctlvm = [x for x in sysctlvm if x.startswith("vm")] | |
| sysctlvm = [x.split(":") for x in sysctlvm] | |
| sysctlvm = [[y.strip() for y in x] for x in sysctlvm] | |
| sysctlvm = [x for x in sysctlvm if x[1]] # If x[1] not empty | |
| ret = {} | |
| for line in sysctlvm: | |
| ret[line[0]] = line[1] | |
| # Special handling for vm.total as it's especially important | |
| sysctlvmtot = __salt__["cmd.run"]("sysctl -n vm.vmtotal").splitlines() | |
| sysctlvmtot = [x for x in sysctlvmtot if x] | |
| ret["vm.vmtotal"] = sysctlvmtot | |
| return ret | |
| def aix_meminfo(): | |
| """ | |
| AIX specific implementation of meminfo | |
| """ | |
| ret = {} | |
| ret["svmon"] = [] | |
| ret["vmstat"] = [] | |
| procn = None | |
| fields = [] | |
| pagesize_flag = False | |
| for line in __salt__["cmd.run"]("svmon -G").splitlines(): | |
| # Note: svmon is per-system | |
| # size inuse free pin virtual mmode | |
| # memory 1048576 1039740 8836 285078 474993 Ded | |
| # pg space 917504 2574 | |
| # | |
| # work pers clnt other | |
| # pin 248379 0 2107 34592 | |
| # in use 474993 0 564747 | |
| # | |
| # PageSize PoolSize inuse pgsp pin virtual | |
| # s 4 KB - 666956 2574 60726 102209 | |
| # m 64 KB - 23299 0 14022 23299 | |
| if not line: | |
| continue | |
| if re.match(r"\s", line): | |
| # assume fields line | |
| fields = line.split() | |
| continue | |
| if line.startswith("memory") or line.startswith("pin"): | |
| procn = len(ret["svmon"]) | |
| ret["svmon"].append({}) | |
| comps = line.split() | |
| ret["svmon"][procn][comps[0]] = {} | |
| for idx, field in enumerate(fields): | |
| if len(comps) > idx + 1: | |
| ret["svmon"][procn][comps[0]][field] = comps[idx + 1] | |
| continue | |
| if line.startswith("pg space") or line.startswith("in use"): | |
| procn = len(ret["svmon"]) | |
| ret["svmon"].append({}) | |
| comps = line.split() | |
| pg_space = "{} {}".format(comps[0], comps[1]) | |
| ret["svmon"][procn][pg_space] = {} | |
| for idx, field in enumerate(fields): | |
| if len(comps) > idx + 2: | |
| ret["svmon"][procn][pg_space][field] = comps[idx + 2] | |
| continue | |
| if line.startswith("PageSize"): | |
| fields = line.split() | |
| pagesize_flag = False | |
| continue | |
| if pagesize_flag: | |
| procn = len(ret["svmon"]) | |
| ret["svmon"].append({}) | |
| comps = line.split() | |
| ret["svmon"][procn][comps[0]] = {} | |
| for idx, field in enumerate(fields): | |
| if len(comps) > idx: | |
| ret["svmon"][procn][comps[0]][field] = comps[idx] | |
| continue | |
| for line in __salt__["cmd.run"]("vmstat -v").splitlines(): | |
| # Note: vmstat is per-system | |
| if not line: | |
| continue | |
| procn = len(ret["vmstat"]) | |
| ret["vmstat"].append({}) | |
| comps = line.lstrip().split(" ", 1) | |
| ret["vmstat"][procn][comps[1]] = comps[0] | |
| return ret | |
| def openbsd_meminfo(): | |
| """ | |
| openbsd specific implementation of meminfo | |
| """ | |
| vmstat = __salt__["cmd.run"]("vmstat").splitlines() | |
| # We're only interested in memory and page values which are printed | |
| # as subsequent fields. | |
| fields = [ | |
| "active virtual pages", | |
| "free list size", | |
| "page faults", | |
| "pages reclaimed", | |
| "pages paged in", | |
| "pages paged out", | |
| "pages freed", | |
| "pages scanned", | |
| ] | |
| data = vmstat[2].split()[2:10] | |
| ret = dict(zip(fields, data)) | |
| return ret | |
| # dict that return a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_meminfo, | |
| "FreeBSD": freebsd_meminfo, | |
| "Junos": freebsd_meminfo, | |
| "OpenBSD": openbsd_meminfo, | |
| "AIX": aix_meminfo, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def cpuinfo(): | |
| """ | |
| .. versionchanged:: 2016.3.2 | |
| Return the CPU info for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| .. versionchanged:: 2018.3.0 | |
| Added support for NetBSD and OpenBSD | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.cpuinfo | |
| """ | |
| def linux_cpuinfo(): | |
| """ | |
| linux specific cpuinfo implementation | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/cpuinfo", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split(":") | |
| comps[0] = comps[0].strip() | |
| if comps[0] == "flags": | |
| ret[comps[0]] = comps[1].split() | |
| else: | |
| ret[comps[0]] = comps[1].strip() | |
| return ret | |
| def bsd_cpuinfo(): | |
| """ | |
| bsd specific cpuinfo implementation | |
| """ | |
| bsd_cmd = "sysctl hw.model hw.ncpu" | |
| ret = {} | |
| if __grains__["kernel"].lower() in ["netbsd", "openbsd"]: | |
| sep = "=" | |
| else: | |
| sep = ":" | |
| for line in __salt__["cmd.run"](bsd_cmd).splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split(sep) | |
| comps[0] = comps[0].strip() | |
| ret[comps[0]] = comps[1].strip() | |
| return ret | |
| def sunos_cpuinfo(): | |
| """ | |
| sunos specific cpuinfo implementation | |
| """ | |
| ret = {} | |
| ret["isainfo"] = {} | |
| for line in __salt__["cmd.run"]("isainfo -x").splitlines(): | |
| # Note: isainfo is per-system and not per-cpu | |
| # Output Example: | |
| # amd64: rdrand f16c vmx avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp cx16 sse3 sse2 sse fxsr mmx cmov amd_sysc cx8 tsc fpu | |
| # i386: rdrand f16c vmx avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp ahf cx16 sse3 sse2 sse fxsr mmx cmov sep cx8 tsc fpu | |
| if not line: | |
| continue | |
| comps = line.split(":") | |
| comps[0] = comps[0].strip() | |
| ret["isainfo"][comps[0]] = sorted(comps[1].strip().split()) | |
| ret["psrinfo"] = [] | |
| procn = None | |
| for line in __salt__["cmd.run"]("psrinfo -v -p").splitlines(): | |
| # Output Example: | |
| # The physical processor has 6 cores and 12 virtual processors (0-5 12-17) | |
| # The core has 2 virtual processors (0 12) | |
| # The core has 2 virtual processors (1 13) | |
| # The core has 2 virtual processors (2 14) | |
| # The core has 2 virtual processors (3 15) | |
| # The core has 2 virtual processors (4 16) | |
| # The core has 2 virtual processors (5 17) | |
| # x86 (GenuineIntel 306E4 family 6 model 62 step 4 clock 2100 MHz) | |
| # Intel(r) Xeon(r) CPU E5-2620 v2 @ 2.10GHz | |
| # The physical processor has 6 cores and 12 virtual processors (6-11 18-23) | |
| # The core has 2 virtual processors (6 18) | |
| # The core has 2 virtual processors (7 19) | |
| # The core has 2 virtual processors (8 20) | |
| # The core has 2 virtual processors (9 21) | |
| # The core has 2 virtual processors (10 22) | |
| # The core has 2 virtual processors (11 23) | |
| # x86 (GenuineIntel 306E4 family 6 model 62 step 4 clock 2100 MHz) | |
| # Intel(r) Xeon(r) CPU E5-2620 v2 @ 2.10GHz | |
| # | |
| # Output Example 2: | |
| # The physical processor has 4 virtual processors (0-3) | |
| # x86 (GenuineIntel 406D8 family 6 model 77 step 8 clock 2400 MHz) | |
| # Intel(r) Atom(tm) CPU C2558 @ 2.40GHz | |
| if not line: | |
| continue | |
| if line.startswith("The physical processor"): | |
| procn = len(ret["psrinfo"]) | |
| line = line.split() | |
| ret["psrinfo"].append({}) | |
| if "cores" in line: | |
| ret["psrinfo"][procn]["topology"] = {} | |
| ret["psrinfo"][procn]["topology"]["cores"] = _number(line[4]) | |
| ret["psrinfo"][procn]["topology"]["threads"] = _number(line[7]) | |
| elif "virtual" in line: | |
| ret["psrinfo"][procn]["topology"] = {} | |
| ret["psrinfo"][procn]["topology"]["threads"] = _number(line[4]) | |
| elif line.startswith(" " * 6): # 3x2 space indent | |
| ret["psrinfo"][procn]["name"] = line.strip() | |
| elif line.startswith(" " * 4): # 2x2 space indent | |
| line = line.strip().split() | |
| ret["psrinfo"][procn]["vendor"] = line[1][1:] | |
| ret["psrinfo"][procn]["family"] = _number(line[4]) | |
| ret["psrinfo"][procn]["model"] = _number(line[6]) | |
| ret["psrinfo"][procn]["step"] = _number(line[8]) | |
| ret["psrinfo"][procn]["clock"] = "{} {}".format(line[10], line[11][:-1]) | |
| return ret | |
| def aix_cpuinfo(): | |
| """ | |
| AIX specific cpuinfo implementation | |
| """ | |
| ret = {} | |
| ret["prtconf"] = [] | |
| ret["lparstat"] = [] | |
| procn = None | |
| for line in __salt__["cmd.run"]( | |
| 'prtconf | grep -i "Processor"', python_shell=True | |
| ).splitlines(): | |
| # Note: prtconf is per-system and not per-cpu | |
| # Output Example: | |
| # prtconf | grep -i "Processor" | |
| # Processor Type: PowerPC_POWER7 | |
| # Processor Implementation Mode: POWER 7 | |
| # Processor Version: PV_7_Compat | |
| # Number Of Processors: 2 | |
| # Processor Clock Speed: 3000 MHz | |
| # Model Implementation: Multiple Processor, PCI bus | |
| # + proc0 Processor | |
| # + proc4 Processor | |
| if not line: | |
| continue | |
| procn = len(ret["prtconf"]) | |
| if line.startswith("Processor") or line.startswith("Number"): | |
| ret["prtconf"].append({}) | |
| comps = line.split(":") | |
| comps[0] = comps[0].rstrip() | |
| ret["prtconf"][procn][comps[0]] = comps[1] | |
| else: | |
| continue | |
| for line in __salt__["cmd.run"]( | |
| 'prtconf | grep "CPU"', python_shell=True | |
| ).splitlines(): | |
| # Note: prtconf is per-system and not per-cpu | |
| # Output Example: | |
| # CPU Type: 64-bit | |
| if not line: | |
| continue | |
| procn = len(ret["prtconf"]) | |
| if line.startswith("CPU"): | |
| ret["prtconf"].append({}) | |
| comps = line.split(":") | |
| comps[0] = comps[0].rstrip() | |
| ret["prtconf"][procn][comps[0]] = comps[1] | |
| else: | |
| continue | |
| for line in __salt__["cmd.run"]( | |
| "lparstat -i | grep CPU", python_shell=True | |
| ).splitlines(): | |
| # Note: lparstat is per-system and not per-cpu | |
| # Output Example: | |
| # Online Virtual CPUs : 2 | |
| # Maximum Virtual CPUs : 2 | |
| # Minimum Virtual CPUs : 1 | |
| # Maximum Physical CPUs in system : 32 | |
| # Active Physical CPUs in system : 32 | |
| # Active CPUs in Pool : 32 | |
| # Shared Physical CPUs in system : 32 | |
| # Physical CPU Percentage : 25.00% | |
| # Desired Virtual CPUs : 2 | |
| if not line: | |
| continue | |
| procn = len(ret["lparstat"]) | |
| ret["lparstat"].append({}) | |
| comps = line.split(":") | |
| comps[0] = comps[0].rstrip() | |
| ret["lparstat"][procn][comps[0]] = comps[1] | |
| return ret | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_cpuinfo, | |
| "FreeBSD": bsd_cpuinfo, | |
| "Junos": bsd_cpuinfo, | |
| "NetBSD": bsd_cpuinfo, | |
| "OpenBSD": bsd_cpuinfo, | |
| "SunOS": sunos_cpuinfo, | |
| "AIX": aix_cpuinfo, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def diskstats(): | |
| """ | |
| .. versionchanged:: 2016.3.2 | |
| Return the disk stats for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.diskstats | |
| """ | |
| def linux_diskstats(): | |
| """ | |
| linux specific implementation of diskstats | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/diskstats", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split() | |
| ret[comps[2]] = { | |
| "major": _number(comps[0]), | |
| "minor": _number(comps[1]), | |
| "device": _number(comps[2]), | |
| "reads_issued": _number(comps[3]), | |
| "reads_merged": _number(comps[4]), | |
| "sectors_read": _number(comps[5]), | |
| "ms_spent_reading": _number(comps[6]), | |
| "writes_completed": _number(comps[7]), | |
| "writes_merged": _number(comps[8]), | |
| "sectors_written": _number(comps[9]), | |
| "ms_spent_writing": _number(comps[10]), | |
| "io_in_progress": _number(comps[11]), | |
| "ms_spent_in_io": _number(comps[12]), | |
| "weighted_ms_spent_in_io": _number(comps[13]), | |
| } | |
| return ret | |
| def generic_diskstats(): | |
| """ | |
| generic implementation of diskstats | |
| note: freebsd and sunos | |
| """ | |
| ret = {} | |
| iostat = __salt__["cmd.run"]("iostat -xzd").splitlines() | |
| header = iostat[1] | |
| for line in iostat[2:]: | |
| comps = line.split() | |
| ret[comps[0]] = {} | |
| for metric, value in zip(header.split()[1:], comps[1:]): | |
| ret[comps[0]][metric] = _number(value) | |
| return ret | |
| def aix_diskstats(): | |
| """ | |
| AIX specific implementation of diskstats | |
| """ | |
| ret = {} | |
| procn = None | |
| fields = [] | |
| disk_name = "" | |
| disk_mode = "" | |
| for line in __salt__["cmd.run"]("iostat -dDV").splitlines(): | |
| # Note: iostat -dDV is per-system | |
| # | |
| # System configuration: lcpu=8 drives=1 paths=2 vdisks=2 | |
| # | |
| # hdisk0 xfer: %tm_act bps tps bread bwrtn | |
| # 0.0 0.8 0.0 0.0 0.8 | |
| # read: rps avgserv minserv maxserv timeouts fails | |
| # 0.0 2.5 0.3 12.4 0 0 | |
| # write: wps avgserv minserv maxserv timeouts fails | |
| # 0.0 0.3 0.2 0.7 0 0 | |
| # queue: avgtime mintime maxtime avgwqsz avgsqsz sqfull | |
| # 0.3 0.0 5.3 0.0 0.0 0.0 | |
| # -------------------------------------------------------------------------------- | |
| if not line or line.startswith("System") or line.startswith("-----------"): | |
| continue | |
| if not re.match(r"\s", line): | |
| # have new disk | |
| dsk_comps = line.split(":") | |
| dsk_firsts = dsk_comps[0].split() | |
| disk_name = dsk_firsts[0] | |
| disk_mode = dsk_firsts[1] | |
| fields = dsk_comps[1].split() | |
| ret[disk_name] = [] | |
| procn = len(ret[disk_name]) | |
| ret[disk_name].append({}) | |
| ret[disk_name][procn][disk_mode] = {} | |
| continue | |
| if ":" in line: | |
| comps = line.split(":") | |
| fields = comps[1].split() | |
| disk_mode = comps[0].lstrip() | |
| procn = len(ret[disk_name]) | |
| ret[disk_name].append({}) | |
| ret[disk_name][procn][disk_mode] = {} | |
| else: | |
| comps = line.split() | |
| for idx, field in enumerate(fields): | |
| if len(comps) > idx: | |
| ret[disk_name][procn][disk_mode][field] = comps[idx] | |
| return ret | |
| # dict that return a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_diskstats, | |
| "FreeBSD": generic_diskstats, | |
| "Junos": generic_diskstats, | |
| "SunOS": generic_diskstats, | |
| "AIX": aix_diskstats, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def diskusage(*args): | |
| """ | |
| Return the disk usage for this minion | |
| Usage:: | |
| salt '*' status.diskusage [paths and/or filesystem types] | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.diskusage # usage for all filesystems | |
| salt '*' status.diskusage / /tmp # usage for / and /tmp | |
| salt '*' status.diskusage ext? # usage for ext[234] filesystems | |
| salt '*' status.diskusage / ext? # usage for / and all ext filesystems | |
| """ | |
| selected = set() | |
| fstypes = set() | |
| if not args: | |
| # select all filesystems | |
| fstypes.add("*") | |
| else: | |
| for arg in args: | |
| if arg.startswith("/"): | |
| # select path | |
| selected.add(arg) | |
| else: | |
| # select fstype | |
| fstypes.add(arg) | |
| if fstypes: | |
| # determine which mount points host the specified fstypes | |
| regex = re.compile( | |
| "|".join(fnmatch.translate(fstype).format("(%s)") for fstype in fstypes) | |
| ) | |
| # ifile source of data varies with OS, otherwise all the same | |
| if __grains__["kernel"] == "Linux": | |
| try: | |
| with salt.utils.files.fopen("/proc/mounts", "r") as fp_: | |
| ifile = salt.utils.stringutils.to_unicode(fp_.read()).splitlines() | |
| except OSError: | |
| return {} | |
| elif __grains__["kernel"] in ("FreeBSD", "SunOS"): | |
| ifile = __salt__["cmd.run"]("mount -p").splitlines() | |
| else: | |
| raise CommandExecutionError( | |
| "status.diskusage not yet supported on this platform" | |
| ) | |
| for line in ifile: | |
| comps = line.split() | |
| if __grains__["kernel"] == "SunOS": | |
| if len(comps) >= 4: | |
| mntpt = comps[2] | |
| fstype = comps[3] | |
| if regex.match(fstype): | |
| selected.add(mntpt) | |
| else: | |
| if len(comps) >= 3: | |
| mntpt = comps[1] | |
| fstype = comps[2] | |
| if regex.match(fstype): | |
| selected.add(mntpt) | |
| # query the filesystems disk usage | |
| ret = {} | |
| for path in selected: | |
| fsstats = os.statvfs(path) | |
| blksz = fsstats.f_bsize | |
| available = fsstats.f_bavail * blksz | |
| total = fsstats.f_blocks * blksz | |
| ret[path] = {"available": available, "total": total} | |
| return ret | |
| def vmstats(): | |
| """ | |
| .. versionchanged:: 2016.3.2 | |
| Return the virtual memory stats for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.vmstats | |
| """ | |
| def linux_vmstats(): | |
| """ | |
| linux specific implementation of vmstats | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/vmstat", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split() | |
| ret[comps[0]] = _number(comps[1]) | |
| return ret | |
| def generic_vmstats(): | |
| """ | |
| generic implementation of vmstats | |
| note: works on FreeBSD, SunOS and OpenBSD (possibly others) | |
| """ | |
| ret = {} | |
| for line in __salt__["cmd.run"]("vmstat -s").splitlines(): | |
| comps = line.split() | |
| if comps[0].isdigit(): | |
| ret[" ".join(comps[1:])] = _number(comps[0].strip()) | |
| return ret | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_vmstats, | |
| "FreeBSD": generic_vmstats, | |
| "Junos": generic_vmstats, | |
| "OpenBSD": generic_vmstats, | |
| "SunOS": generic_vmstats, | |
| "AIX": generic_vmstats, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def nproc(): | |
| """ | |
| Return the number of processing units available on this system | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| .. versionchanged:: 2018.3.0 | |
| Added support for Darwin, FreeBSD and OpenBSD | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.nproc | |
| """ | |
| def linux_nproc(): | |
| """ | |
| linux specific implementation of nproc | |
| """ | |
| try: | |
| return _number(__salt__["cmd.run"]("nproc").strip()) | |
| except ValueError: | |
| return 0 | |
| def generic_nproc(): | |
| """ | |
| generic implementation of nproc | |
| """ | |
| ncpu_data = __salt__["sysctl.get"]("hw.ncpu") | |
| if not ncpu_data: | |
| # We need at least one CPU to run | |
| return 1 | |
| else: | |
| return _number(ncpu_data) | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_nproc, | |
| "Darwin": generic_nproc, | |
| "FreeBSD": generic_nproc, | |
| "Junos": generic_nproc, | |
| "OpenBSD": generic_nproc, | |
| "AIX": _aix_nproc, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def netstats(): | |
| """ | |
| Return the network stats for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| .. versionchanged:: 2018.3.0 | |
| Added support for OpenBSD | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.netstats | |
| """ | |
| def linux_netstats(): | |
| """ | |
| linux specific netstats implementation | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/net/netstat", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| headers = [""] | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| comps = line.split() | |
| if comps[0] == headers[0]: | |
| index = len(headers) - 1 | |
| row = {} | |
| for field in range(index): | |
| if field < 1: | |
| continue | |
| else: | |
| row[headers[field]] = _number(comps[field]) | |
| rowname = headers[0].replace(":", "") | |
| ret[rowname] = row | |
| else: | |
| headers = comps | |
| return ret | |
| def freebsd_netstats(): | |
| return bsd_netstats() | |
| def bsd_netstats(): | |
| """ | |
| bsd specific netstats implementation | |
| """ | |
| ret = {} | |
| for line in __salt__["cmd.run"]("netstat -s").splitlines(): | |
| if line.startswith("\t\t"): | |
| continue # Skip, too detailed | |
| if not line.startswith("\t"): | |
| key = line.split()[0].replace(":", "") | |
| ret[key] = {} | |
| else: | |
| comps = line.split() | |
| if comps[0].isdigit(): | |
| ret[key][" ".join(comps[1:])] = comps[0] | |
| return ret | |
| def sunos_netstats(): | |
| """ | |
| sunos specific netstats implementation | |
| """ | |
| ret = {} | |
| for line in __salt__["cmd.run"]("netstat -s").splitlines(): | |
| line = line.replace("=", " = ").split() | |
| if len(line) > 6: | |
| line.pop(0) | |
| if "=" in line: | |
| if len(line) >= 3: | |
| if line[2].isdigit() or line[2][0] == "-": | |
| line[2] = _number(line[2]) | |
| ret[line[0]] = line[2] | |
| if len(line) >= 6: | |
| if line[5].isdigit() or line[5][0] == "-": | |
| line[5] = _number(line[5]) | |
| ret[line[3]] = line[5] | |
| return ret | |
| def aix_netstats(): | |
| """ | |
| AIX specific netstats implementation | |
| """ | |
| ret = {} | |
| fields = [] | |
| procn = None | |
| proto_name = None | |
| for line in __salt__["cmd.run"]("netstat -s").splitlines(): | |
| if not line: | |
| continue | |
| if not re.match(r"\s", line) and ":" in line: | |
| comps = line.split(":") | |
| proto_name = comps[0] | |
| ret[proto_name] = [] | |
| procn = len(ret[proto_name]) | |
| ret[proto_name].append({}) | |
| continue | |
| else: | |
| comps = line.split() | |
| comps[0] = comps[0].strip() | |
| if comps[0].isdigit(): | |
| ret[proto_name][procn][" ".join(comps[1:])] = _number(comps[0]) | |
| else: | |
| continue | |
| return ret | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_netstats, | |
| "FreeBSD": bsd_netstats, | |
| "Junos": bsd_netstats, | |
| "OpenBSD": bsd_netstats, | |
| "SunOS": sunos_netstats, | |
| "AIX": aix_netstats, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def netdev(): | |
| """ | |
| .. versionchanged:: 2016.3.2 | |
| Return the network device stats for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.netdev | |
| """ | |
| def linux_netdev(): | |
| """ | |
| linux specific implementation of netdev | |
| """ | |
| ret = {} | |
| try: | |
| with salt.utils.files.fopen("/proc/net/dev", "r") as fp_: | |
| stats = salt.utils.stringutils.to_unicode(fp_.read()) | |
| except OSError: | |
| pass | |
| else: | |
| for line in stats.splitlines(): | |
| if not line: | |
| continue | |
| if line.find(":") < 0: | |
| continue | |
| comps = line.split() | |
| # Fix lines like eth0:9999..' | |
| comps[0] = line.split(":")[0].strip() | |
| # Support lines both like eth0:999 and eth0: 9999 | |
| comps.insert(1, line.split(":")[1].strip().split()[0]) | |
| ret[comps[0]] = { | |
| "iface": comps[0], | |
| "rx_bytes": _number(comps[2]), | |
| "rx_compressed": _number(comps[8]), | |
| "rx_drop": _number(comps[5]), | |
| "rx_errs": _number(comps[4]), | |
| "rx_fifo": _number(comps[6]), | |
| "rx_frame": _number(comps[7]), | |
| "rx_multicast": _number(comps[9]), | |
| "rx_packets": _number(comps[3]), | |
| "tx_bytes": _number(comps[10]), | |
| "tx_carrier": _number(comps[16]), | |
| "tx_colls": _number(comps[15]), | |
| "tx_compressed": _number(comps[17]), | |
| "tx_drop": _number(comps[13]), | |
| "tx_errs": _number(comps[12]), | |
| "tx_fifo": _number(comps[14]), | |
| "tx_packets": _number(comps[11]), | |
| } | |
| return ret | |
| def freebsd_netdev(): | |
| """ | |
| freebsd specific implementation of netdev | |
| """ | |
| _dict_tree = lambda: collections.defaultdict(_dict_tree) | |
| ret = _dict_tree() | |
| netstat = __salt__["cmd.run"]("netstat -i -n -4 -b -d").splitlines() | |
| netstat += __salt__["cmd.run"]("netstat -i -n -6 -b -d").splitlines()[1:] | |
| header = netstat[0].split() | |
| for line in netstat[1:]: | |
| comps = line.split() | |
| for i in range(4, 13): # The columns we want | |
| ret[comps[0]][comps[2]][comps[3]][header[i]] = _number(comps[i]) | |
| return ret | |
| def sunos_netdev(): | |
| """ | |
| sunos specific implementation of netdev | |
| """ | |
| ret = {} | |
| ##NOTE: we cannot use hwaddr_interfaces here, so we grab both ip4 and ip6 | |
| for dev in itertools.chain( | |
| __grains__["ip4_interfaces"].keys(), __grains__["ip6_interfaces"].keys() | |
| ): | |
| # fetch device info | |
| netstat_ipv4 = __salt__["cmd.run"]( | |
| "netstat -i -I {dev} -n -f inet".format(dev=dev) | |
| ).splitlines() | |
| netstat_ipv6 = __salt__["cmd.run"]( | |
| "netstat -i -I {dev} -n -f inet6".format(dev=dev) | |
| ).splitlines() | |
| # prepare data | |
| netstat_ipv4[0] = netstat_ipv4[0].split() | |
| netstat_ipv4[1] = netstat_ipv4[1].split() | |
| netstat_ipv6[0] = netstat_ipv6[0].split() | |
| netstat_ipv6[1] = netstat_ipv6[1].split() | |
| # add data | |
| ret[dev] = {} | |
| for val in netstat_ipv4[0][:-1]: | |
| if val == "Name": | |
| continue | |
| if val in ["Address", "Net/Dest"]: | |
| ret[dev]["IPv4 {field}".format(field=val)] = val | |
| else: | |
| ret[dev][val] = _number(val) | |
| for val in netstat_ipv6[0][:-1]: | |
| if val == "Name": | |
| continue | |
| if val in ["Address", "Net/Dest"]: | |
| ret[dev]["IPv6 {field}".format(field=val)] = val | |
| else: | |
| ret[dev][val] = _number(val) | |
| return ret | |
| def aix_netdev(): | |
| """ | |
| AIX specific implementation of netdev | |
| """ | |
| ret = {} | |
| fields = [] | |
| procn = None | |
| for dev in itertools.chain( | |
| __grains__["ip4_interfaces"].keys(), __grains__["ip6_interfaces"].keys() | |
| ): | |
| # fetch device info | |
| # root@la68pp002_pub:# netstat -i -n -I en0 -f inet | |
| # Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll | |
| # en0 1500 link#3 e2.eb.32.42.84.c 10029668 0 446490 0 0 | |
| # en0 1500 172.29.128 172.29.149.95 10029668 0 446490 0 0 | |
| # root@la68pp002_pub:# netstat -i -n -I en0 -f inet6 | |
| # Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll | |
| # en0 1500 link#3 e2.eb.32.42.84.c 10029731 0 446499 0 0 | |
| netstat_ipv4 = __salt__["cmd.run"]( | |
| "netstat -i -n -I {dev} -f inet".format(dev=dev) | |
| ).splitlines() | |
| netstat_ipv6 = __salt__["cmd.run"]( | |
| "netstat -i -n -I {dev} -f inet6".format(dev=dev) | |
| ).splitlines() | |
| # add data | |
| ret[dev] = [] | |
| for line in netstat_ipv4: | |
| if line.startswith("Name"): | |
| fields = line.split() | |
| continue | |
| comps = line.split() | |
| if len(comps) < 3: | |
| raise CommandExecutionError( | |
| "Insufficent data returned by command to process '{}'".format( | |
| line | |
| ) | |
| ) | |
| if comps[2].startswith("link"): | |
| continue | |
| procn = len(ret[dev]) | |
| ret[dev].append({}) | |
| ret[dev][procn]["ipv4"] = {} | |
| for i in range(1, len(fields)): | |
| if len(comps) > i: | |
| ret[dev][procn]["ipv4"][fields[i]] = comps[i] | |
| for line in netstat_ipv6: | |
| if line.startswith("Name"): | |
| fields = line.split() | |
| continue | |
| comps = line.split() | |
| if len(comps) < 3: | |
| raise CommandExecutionError( | |
| "Insufficent data returned by command to process '{}'".format( | |
| line | |
| ) | |
| ) | |
| if comps[2].startswith("link"): | |
| continue | |
| procn = len(ret[dev]) | |
| ret[dev].append({}) | |
| ret[dev][procn]["ipv6"] = {} | |
| for i in range(1, len(fields)): | |
| if len(comps) > i: | |
| ret[dev][procn]["ipv6"][fields[i]] = comps[i] | |
| return ret | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_netdev, | |
| "FreeBSD": freebsd_netdev, | |
| "Junos": freebsd_netdev, | |
| "SunOS": sunos_netdev, | |
| "AIX": aix_netdev, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def w(): # pylint: disable=C0103 | |
| """ | |
| Return a list of logged in users for this minion, using the w command | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.w | |
| """ | |
| def linux_w(): | |
| """ | |
| Linux specific implementation for w | |
| """ | |
| user_list = [] | |
| users = __salt__["cmd.run"]("w -fh").splitlines() | |
| for row in users: | |
| if not row: | |
| continue | |
| comps = row.split() | |
| rec = { | |
| "idle": comps[3], | |
| "jcpu": comps[4], | |
| "login": comps[2], | |
| "pcpu": comps[5], | |
| "tty": comps[1], | |
| "user": comps[0], | |
| "what": " ".join(comps[6:]), | |
| } | |
| user_list.append(rec) | |
| return user_list | |
| def bsd_w(): | |
| """ | |
| Generic BSD implementation for w | |
| """ | |
| user_list = [] | |
| users = __salt__["cmd.run"]("w -h").splitlines() | |
| for row in users: | |
| if not row: | |
| continue | |
| comps = row.split() | |
| rec = { | |
| "from": comps[2], | |
| "idle": comps[4], | |
| "login": comps[3], | |
| "tty": comps[1], | |
| "user": comps[0], | |
| "what": " ".join(comps[5:]), | |
| } | |
| user_list.append(rec) | |
| return user_list | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Darwin": bsd_w, | |
| "FreeBSD": bsd_w, | |
| "Junos": bsd_w, | |
| "Linux": linux_w, | |
| "OpenBSD": bsd_w, | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def all_status(): | |
| """ | |
| Return a composite of all status data and info for this minion. | |
| Warning: There is a LOT here! | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.all_status | |
| """ | |
| return { | |
| "cpuinfo": cpuinfo(), | |
| "cpustats": cpustats(), | |
| "diskstats": diskstats(), | |
| "diskusage": diskusage(), | |
| "loadavg": loadavg(), | |
| "meminfo": meminfo(), | |
| "netdev": netdev(), | |
| "netstats": netstats(), | |
| "uptime": uptime(), | |
| "vmstats": vmstats(), | |
| "w": w(), | |
| } | |
| def pid(sig): | |
| """ | |
| Return the PID or an empty string if the process is running or not. | |
| Pass a signature to use to find the process via ps. Note you can pass | |
| a Python-compatible regular expression to return all pids of | |
| processes matching the regexp. | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.pid <sig> | |
| """ | |
| my_pid = str(os.getpid()) | |
| cmd = __grains__["ps"] | |
| output = __salt__["cmd.run_stdout"](cmd, python_shell=True) | |
| pids = "" | |
| for line in output.splitlines(): | |
| if my_pid in line: | |
| continue | |
| if re.search(sig, line): | |
| if pids: | |
| pids += "\n" | |
| pids += line.split()[1] | |
| return pids | |
| def version(): | |
| """ | |
| Return the system version for this minion | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| .. versionchanged:: 2018.3.0 | |
| Added support for OpenBSD | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.version | |
| """ | |
| def linux_version(): | |
| """ | |
| linux specific implementation of version | |
| """ | |
| try: | |
| with salt.utils.files.fopen("/proc/version", "r") as fp_: | |
| return salt.utils.stringutils.to_unicode(fp_.read()).strip() | |
| except OSError: | |
| return {} | |
| def bsd_version(): | |
| """ | |
| bsd specific implementation of version | |
| """ | |
| return __salt__["cmd.run"]("sysctl -n kern.version") | |
| # dict that returns a function that does the right thing per platform | |
| get_version = { | |
| "Linux": linux_version, | |
| "FreeBSD": bsd_version, | |
| "Junos": bsd_version, | |
| "OpenBSD": bsd_version, | |
| "AIX": lambda: __salt__["cmd.run"]("oslevel -s"), | |
| } | |
| errmsg = "This method is unsupported on the current operating system!" | |
| return get_version.get(__grains__["kernel"], lambda: errmsg)() | |
| def master(master=None, connected=True): | |
| """ | |
| .. versionadded:: 2014.7.0 | |
| Return the connection status with master. Fire an event if the | |
| connection to master is not as expected. This function is meant to be | |
| run via a scheduled job from the minion. If master_ip is an FQDN/Hostname, | |
| it must be resolvable to a valid IPv4 address. | |
| .. versionchanged:: 2016.11.4 | |
| Added support for AIX | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.master | |
| """ | |
| master_ips = None | |
| if master: | |
| master_ips = salt.utils.network.host_to_ips(master) | |
| if not master_ips: | |
| return | |
| master_connection_status = False | |
| port = __salt__["config.get"]("publish_port", default=4505) | |
| connected_ips = salt.utils.network.remote_port_tcp(port) | |
| # Get connection status for master | |
| for master_ip in master_ips: | |
| if master_ip in connected_ips: | |
| master_connection_status = True | |
| break | |
| # Connection to master is not as expected | |
| if master_connection_status is not connected: | |
| with salt.utils.event.get_event("minion", opts=__opts__, listen=False) as event: | |
| if master_connection_status: | |
| event.fire_event( | |
| {"master": master}, salt.minion.master_event(type="connected") | |
| ) | |
| else: | |
| event.fire_event( | |
| {"master": master}, salt.minion.master_event(type="disconnected") | |
| ) | |
| return master_connection_status | |
| def ping_master(master): | |
| """ | |
| .. versionadded:: 2016.3.0 | |
| Sends ping request to the given master. Fires '__master_failback' event on success. | |
| Returns bool result. | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.ping_master localhost | |
| """ | |
| if master is None or master == "": | |
| return False | |
| opts = copy.deepcopy(__opts__) | |
| opts["master"] = master | |
| if "master_ip" in opts: # avoid 'master ip changed' warning | |
| del opts["master_ip"] | |
| opts.update(salt.minion.prep_ip_port(opts)) | |
| try: | |
| opts.update(salt.minion.resolve_dns(opts, fallback=False)) | |
| except Exception: # pylint: disable=broad-except | |
| return False | |
| timeout = opts.get("auth_timeout", 60) | |
| load = {"cmd": "ping"} | |
| result = False | |
| with salt.channel.client.ReqChannel.factory(opts, crypt="clear") as channel: | |
| try: | |
| payload = channel.send(load, tries=0, timeout=timeout) | |
| result = True | |
| except Exception as e: # pylint: disable=broad-except | |
| pass | |
| if result: | |
| with salt.utils.event.get_event( | |
| "minion", opts=__opts__, listen=False | |
| ) as event: | |
| event.fire_event( | |
| {"master": master}, salt.minion.master_event(type="failback") | |
| ) | |
| return result | |
| def proxy_reconnect(proxy_name, opts=None): | |
| """ | |
| Forces proxy minion reconnection when not alive. | |
| proxy_name | |
| The virtual name of the proxy module. | |
| opts: None | |
| Opts dictionary. Not intended for CLI usage. | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.proxy_reconnect rest_sample | |
| """ | |
| if not opts: | |
| opts = __opts__ | |
| if "proxy" not in opts: | |
| return False # fail | |
| proxy_keepalive_fn = proxy_name + ".alive" | |
| if proxy_keepalive_fn not in __proxy__: | |
| return False # fail | |
| chk_reboot_active_key = proxy_name + ".get_reboot_active" | |
| if chk_reboot_active_key in __proxy__ and __proxy__[chk_reboot_active_key](): | |
| # if rebooting or shutting down, don't run proxy_reconnect | |
| # it interferes with the connection and disrupts the shutdown/reboot | |
| # especially | |
| minion_id = opts.get("proxyid", "") or opts.get("id", "") | |
| log.info( | |
| "%s (%s proxy) is rebooting or shutting down. Don't probe connection.", | |
| minion_id, | |
| proxy_name, | |
| ) | |
| return True | |
| is_alive = __proxy__[proxy_keepalive_fn](opts) | |
| if not is_alive: | |
| minion_id = opts.get("proxyid", "") or opts.get("id", "") | |
| log.info("%s (%s proxy) is down. Restarting.", minion_id, proxy_name) | |
| __proxy__[proxy_name + ".shutdown"](opts) # safely close connection | |
| __proxy__[proxy_name + ".init"](opts) # reopen connection | |
| log.debug("Restarted %s (%s proxy)!", minion_id, proxy_name) | |
| return True # success | |
| def time_(format="%A, %d. %B %Y %I:%M%p"): | |
| """ | |
| .. versionadded:: 2016.3.0 | |
| Return the current time on the minion, | |
| formatted based on the format parameter. | |
| Default date format: Monday, 27. July 2015 07:55AM | |
| CLI Example: | |
| .. code-block:: bash | |
| salt '*' status.time | |
| salt '*' status.time '%s' | |
| """ | |
| dt = datetime.datetime.today() | |
| return dt.strftime(format) |