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. * (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. * 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. * 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. * (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.
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.
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.
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]