File: //lib/fm-agent/plugins/memory_usage.py
import agent_util
import os
import re
import sys
import socket
from agent_util import float
def search_esxtop(headers, search_string):
for idx, column in enumerate(headers):
if search_string in column:
return idx
return None
class MemoryUsagePlugin(agent_util.Plugin):
textkey = "memory"
label = "Memory"
# adding minimum RAM and swap usage
min_ram = 0
min_swap = 0
@staticmethod
def _parse_sysctl(stdout):
lines = stdout.split("\n")
sysctl = {}
for line in lines:
m = re.search(r"^(.+?)\s*:\s*(.+)$", line)
if not m:
continue
k = str(m.group(1))
v = m.group(2)
if v.isdigit():
v = int(v)
sysctl[k] = v
return sysctl
@classmethod
def get_metadata(self, config):
if "freebsd" in sys.platform:
sysctl = agent_util.which("sysctl")
status = agent_util.SUPPORTED
msg = None
if not sysctl:
self.log.info("sysctl binary not found")
status = agent_util.UNSUPPORTED
msg = "sysctl binary not found"
return {}
if status is agent_util.SUPPORTED:
ret, output = agent_util.execute_command(sysctl + " -a")
if config.get("debug", False):
self.log.debug(
"#####################################################"
)
self.log.debug("Memory Usage command 'sysctl -a' output:")
self.log.debug(str(output))
self.log.debug(
"#####################################################"
)
if ret != 0 or not output:
status = agent_util.UNSUPPORTED
msg = "error executing 'sysctl -a'"
if status is agent_util.SUPPORTED:
d = self._parse_sysctl(output)
required_keys = (
"hw.pagesize",
"hw.physmem",
"vfs.bufspace",
"vm.stats.vm.v_inactive_count",
"vm.stats.vm.v_active_count",
"vm.stats.vm.v_cache_count",
"vm.stats.vm.v_free_count",
)
# here we're making sure the set of all the keys we need
# are in the set of all keys (subset)
for key in required_keys:
if key not in d.keys():
status = agent_util.UNSUPPORTED
msg = "could not find all the required sysctl keys"
break
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"unit": "percent",
},
"ram.kb_used": {
"label": "RAM used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_buffer": {
"label": "RAM buffer (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_active": {
"label": "RAM active (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_inactive": {
"label": "RAM inactive (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_cached": {
"label": "RAM cached (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
}
return data
elif "darwin" in sys.platform:
status = agent_util.SUPPORTED
msg = ""
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"unit": "percent",
},
"ram.kb_used": {
"label": "RAM used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_active": {
"label": "RAM active (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_inactive": {
"label": "RAM inactive (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
"ram.kb_wired": {
"label": "RAM wired (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
},
}
return data
elif "aix" in sys.platform:
status = agent_util.SUPPORTED
msg = None
# Figure out how much memory we have
retcode, output = agent_util.execute_command("svmon -O,unit=KB")
output = output.split("\n")
max_ram = None
max_swap = None
for line in output:
if line.startswith("memory"):
parts = line.split()
# max ram in kb
max_ram = int(parts[1])
if line.startswith("pg space"):
parts = line.split()
max_swap = int(parts[2])
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"ram.kb_used": {
"label": "RAM used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": max_ram,
},
"swap.percent": {
"label": "Swap percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"swap.kb_used": {
"label": "Swap used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": max_swap,
},
}
return data
elif "sunos" in sys.platform:
status = agent_util.SUPPORTED
msg = None
# Figure out how much memory we have
max_ram = None
retcode, output = agent_util.execute_command("/usr/sbin/prtconf")
output = output.split("\n")
for line in output:
if "Memory" not in line:
continue
fields = line.split()
max_ram = int(fields[2]) * 1024
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"ram.kb_used": {
"label": "RAM used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": max_ram,
},
}
return data
elif "vmware" in sys.platform:
# Default Linux logic
status = agent_util.SUPPORTED
msg = None
ret, mem_str = agent_util.execute_command(
"esxcli hardware memory get | grep 'Physical Memory'"
)
if ret == 0:
ram_total = (float(mem_str.split(":")[1].split()[0]) / 1024) / 1024
else:
status = agent_util.MISCONFIGURED
msg = "ERROR RUNNING ESXCLI: %s" % mem_str
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"ram.mb_used": {
"label": "RAM used (MB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "mB",
"min_value": 0,
"max_value": ram_total,
},
}
return data
elif "hp-ux" in sys.platform:
status = agent_util.SUPPORTED
msg = None
ret, output = agent_util.execute_command("swapinfo -ta")
max_ram = None
max_swap = None
for line in output:
if line.startswith("memory"):
parts = line.split()
# max ram in kb
max_ram = int(parts[1])
if line.startswith("dev"):
parts = line.split()
max_swap = int(parts[1])
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"ram.kb_used": {
"label": "RAM used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": max_ram,
},
"swap.percent": {
"label": "Swap percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"swap.kb_used": {
"label": "Swap used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": max_swap,
},
}
return data
else:
# Default Linux logic
status = agent_util.SUPPORTED
msg = None
if not os.path.exists("/proc/meminfo"):
status = agent_util.MISCONFIGURED
msg = "Enable procfs."
# Get memory info to find max values
lines = open("/proc/meminfo", "r").readlines()
usage_info = {}
for line in lines:
line = line.strip()
m = re.match(r"^(?P<key>.+?):\s+(?P<value>\d+)", line)
usage_info[m.group("key")] = int(m.group("value"))
ram_total = usage_info.get("MemTotal", 0)
swap_total = usage_info.get("SwapTotal", 0)
data = {
"ram.percent": {
"label": "RAM percent usage",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": 100,
},
"ram.kb_used": {
"label": "RAM used (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": ram_total,
},
"ram.kb_buffer": {
"label": "RAM buffer (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": ram_total,
},
"swap.percent": {
"label": "Swap percent usage",
"options": None,
"status": status,
"error_message": msg,
"unit": "percent",
"min_value": 0,
"max_value": 100,
},
"swap.kb_used": {
"label": "Swap used (Kb)",
"options": None,
"status": status,
"error_message": msg,
"min_value": 0,
"max_value": swap_total,
},
"ram.kb_active": {
"label": "RAM active (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": ram_total,
},
"ram.kb_inactive": {
"label": "RAM inactive (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": ram_total,
},
"ram.kb_cached": {
"label": "RAM cached (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": ram_total,
},
"swap.kb_cached": {
"label": "Swap cached (kB)",
"options": None,
"status": status,
"error_message": msg,
"unit": "kB",
"min_value": 0,
"max_value": ram_total,
},
}
return data
def check(self, textkey, data, config):
sysctl = agent_util.which("sysctl")
if "freebsd" in sys.platform:
mem_info = {}
ret, output = agent_util.execute_command(sysctl + " -a")
d = self._parse_sysctl(output)
buffsize = d["vfs.bufspace"]
pagesize = d["hw.pagesize"]
total = d["hw.physmem"]
inactive = d["vm.stats.vm.v_inactive_count"] * pagesize
active = d["vm.stats.vm.v_active_count"] * pagesize
cache = d["vm.stats.vm.v_cache_count"] * pagesize
free = d["vm.stats.vm.v_free_count"] * pagesize
avail = inactive + cache + free
used = total - avail
self.log.debug("buffsize %s" % str(buffsize))
self.log.debug("pagesize %s" % str(pagesize))
self.log.debug("total %s" % str(total))
self.log.debug("inactive %s" % str(inactive))
self.log.debug("active %s" % str(active))
self.log.debug("cache %s" % str(cache))
self.log.debug("free %s" % str(free))
self.log.debug("avail %s" % str(avail))
self.log.debug("used %s" % str(used))
mem_info["ram.kb_used"] = used / 1024.0
if total > 0:
mem_info["ram.percent"] = (float(used) / total) * 100.0
else:
mem_info["ram.percent"] = 0.0
mem_info["ram.kb_active"] = active / 1024.0
mem_info["ram.kb_inactive"] = inactive / 1024.0
mem_info["ram.kb_cached"] = cache / 1024.0
mem_info["ram.kb_buffer"] = buffsize / 1024.0
# total installed RAM in KB
ram_max = int(total) / 1024
return mem_info[textkey]
elif "darwin" in sys.platform:
vmstat = agent_util.which("vm_stat")
ret, output = agent_util.execute_command(vmstat)
pageSize = None
pageToken = "Mach Virtual Memory Statistics:"
vm_data = {}
for line in output.split("\n"):
if line.startswith(pageToken):
try:
pageSize = int(line[len(pageToken) :].split()[-2])
self.log.debug("Memory page size -> {}".format(pageSize))
except Exception as e:
pass
elif ":" in line:
try:
key, val = line.split(":", 1)
val = float(val.strip())
vm_data[key] = val
except:
pass
if pageSize is None:
self.log.error("Could not compute page size")
return None
kbMultiplier = float(pageSize / 1024)
wired = vm_data.get("Pages wired down", 0) * kbMultiplier
active = vm_data.get("Pages active", 0) * kbMultiplier
inactive = vm_data.get("Pages inactive", 0) * kbMultiplier
free = vm_data.get("Pages free", 0) * kbMultiplier
if textkey == "ram.kb_used":
return wired + active
elif textkey == "ram.kb_active":
return active
elif textkey == "ram.kb_inactive":
return inactive
elif textkey == "ram.kb_wired":
return wired
elif textkey == "ram.percent":
return 100.0 * (wired + active) / (wired + active + inactive + free)
return None
elif "aix" in sys.platform:
retcode, output = agent_util.execute_command("svmon -O,unit=KB")
output = output.split("\n")
self.log.debug("svmon output: %s" % output)
for line in output:
if line.startswith("memory"):
parts = line.split()
# max ram in kb
max_ram = int(parts[1])
if textkey == "ram.percent":
return 100.0 * int(parts[5]) / int(parts[1])
elif textkey == "ram.kb_used":
# svmon -G returns counts in 4096 byte pages
return int(parts[2])
if line.startswith("pg space"):
parts = line.split()
if textkey == "swap.percent":
return 100.0 * int(parts[3]) / int(parts[2])
elif textkey == "swap.kb_used":
# svmon -G returns counts in 4096 byte pages
return int(parts[3])
# Unknown AIX textkey
return None
elif "sunos" in sys.platform:
retcode, output = agent_util.execute_command(
"prstat -s rss 1 1 | awk '{print $4}'"
)
output = output.split("\n")
kb_used = 0
for line in output:
if "RSS" in line or not line:
continue
if "G" in line:
used = line.strip("M")
kb_used += (int(used) * 1024) * 1024
elif "M" in line:
used = line.strip("M")
kb_used += int(used) * 1024
elif "K" in line:
used = line.strip("K")
kb_used += int(used)
retcode, output = agent_util.execute_command("/usr/sbin/prtconf")
output = output.split("\n")
for line in output:
if "Memory" not in line:
continue
fields = line.split()
kb_total = int(fields[2]) * 1024
# setting this to its own var in case we do something funky with kb_total or reset it somehwere
max_ram = kb_total
kb_free = kb_total - kb_used
if textkey == "ram.percent":
return 100.0 * (1.0 - float(kb_free) / float(kb_total))
elif textkey == "ram.kb_used":
return kb_used
# Unknown Solaris textkey
return None
elif "vmware" in sys.platform:
hostname = socket.gethostname()
ret, mem_str = agent_util.execute_command(
"esxcli hardware memory get | grep 'Physical Memory'",
cache_timeout=agent_util.DEFAULT_CACHE_TIMEOUT,
)
if ret == 0:
mb_total = (int(mem_str.split(":")[1].split()[0]) / 1024) / 1024
self.log.debug("Found %s MB of RAM installed" % mb_total)
ret, out = agent_util.execute_command(
"esxtop -b -n 2 -d 2", cache_timeout=agent_util.DEFAULT_CACHE_TIMEOUT
)
out_list = out.split("\n")
headers = out_list[0].replace('"', "").split(",")
data = []
for idx, val in enumerate(out_list[::1]):
if not val or val == "":
continue
data = out_list[idx].replace('"', "").split(",")
mb_free_search = r"\\\\%s\Memory\Free MBytes" % hostname
self.log.debug("Searching VMware for %s" % mb_free_search)
mb_free_idx = search_esxtop(headers, mb_free_search)
if mb_free_idx:
self.log.debug("VMware free memory index %s" % mb_free_idx)
mb_free = float(data[mb_free_idx])
else:
self.log.error("Unable to find RAM info from esxcli output")
return None
if textkey == "ram.mb_used":
return mb_total - mb_free
if textkey == "ram.percent":
return ((mb_total - mb_free) / mb_total) * 100.0
if "hp-ux" in sys.platform:
ret, out = agent_util.execute_command("swapinfo -ta")
usage_info = {}
for line in out.splitlines():
if line.startswith("dev"):
l = line.split()
self.log.debug("Swap: %s" % l)
usage_info["swap.kb_used"] = float(l[2])
if l[2] == 0 or l[2] == "-":
usage_info["swap.percent"] = 0
else:
usage_info["swap.percent"] = (float(l[2]) / float(l[1])) * 100.0
if line.startswith("memory"):
l = line.split()
self.log.debug("RAM: %s" % l)
usage_info["ram.kb_used"] = float(l[2])
usage_info["ram.percent"] = (float(l[2]) / float(l[1])) * 100.0
return usage_info.get(textkey, None)
else:
# Default Linux logic
lines = open("/proc/meminfo", "r").readlines()
usage_info = {}
mem_info = {}
for line in lines:
line = line.strip()
m = re.match(r"^(?P<key>.+?):\s+(?P<value>\d+)", line)
usage_info[m.group("key")] = int(m.group("value"))
ram_buffer = usage_info.get("Buffers", 0)
ram_cached = usage_info.get("Cached", 0)
ram_active = usage_info.get("Active", 0)
ram_available = usage_info.get("MemAvailable", 0)
ram_inactive = usage_info.get("Inactive", 0)
ram_total = usage_info.get("MemTotal", 0)
if config.get("percent_usage_override"):
ram_free = usage_info.get("MemFree", 0)
elif "MemAvailable" in usage_info:
ram_free = ram_available
else:
ram_free = usage_info.get("MemFree", 0) + ram_buffer + ram_cached
ram_used = ram_total - ram_free
# setting max_ram to its own var in case we do something funky with ram_total later
max_ram = ram_total
mem_info["ram.kb_buffer"] = ram_buffer
mem_info["ram.kb_used"] = ram_used
if ram_total > 0:
mem_info["ram.percent"] = (float(ram_used) / ram_total) * 100.0
else:
mem_info["ram.percent"] = 0.0
mem_info["ram.kb_active"] = ram_active
mem_info["ram.kb_inactive"] = ram_inactive
mem_info["ram.kb_cached"] = ram_cached
swap_cached = usage_info.get("SwapCached", 0)
swap_total = usage_info.get("SwapTotal", 0)
swap_free = usage_info.get("SwapFree", 0)
swap_used = swap_total - swap_free
# including max amount of swap as well b/c we calc a percentage on that as well
max_swap = swap_total
mem_info["swap.kb_used"] = swap_used
if swap_total > 0:
mem_info["swap.percent"] = (float(swap_used) / swap_total) * 100.0
else:
mem_info["swap.percent"] = 0.0
mem_info["swap.kb_cached"] = swap_cached
self.log.debug("ram_total %s" % str(ram_total))
self.log.debug("ram_free %s" % str(ram_free))
self.log.debug("ram_active %s" % str(ram_active))
self.log.debug("ram_inactive %s" % str(ram_inactive))
self.log.debug("ram_cached %s" % str(ram_cached))
self.log.debug("swap_total %s" % str(swap_total))
self.log.debug("swap_free %s" % str(swap_free))
self.log.debug("swap_used %s" % str(swap_used))
self.log.debug("swap_cached %s" % str(swap_cached))
return mem_info[textkey]