Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions scripts/procdockerstatsd
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,26 @@ class ProcDockerStats(daemon_base.DaemonBase):
process_data_list.append(process_data)
return process_data_list

def format_mount_cmd_output(self, cmdout):
lines = cmdout.splitlines()
keys = re.split(" +", lines[0])
mount_data = dict()
mount_data_list = []
for line in lines[1:]:
values = line.split()
mount_data_list.append(values)
formatted_dict = self.create_mount_dict(mount_data_list)
return formatted_dict

def format_mem_output(self, data):
lines = data.splitlines()
mem_map = {}
for line in lines:
key, value = line.split(":")
mem_map[key] = value.strip().split()[0]
formatted_dict = self.create_mem_dict(mem_map)
return formatted_dict

def convert_to_bytes(self, value):
UNITS_B = 'B'
UNITS_KB = 'KB'
Expand Down Expand Up @@ -120,6 +140,54 @@ class ProcDockerStats(daemon_base.DaemonBase):
dockerdict[key]['PIDS'] = row.get('PIDS')
return dockerdict

def create_mount_dict(self, dict_list):
mountdict = {}
for row in dict_list[0:]:
if row[1] == "tmpfs" or row[1] == "devtmpfs" or row[1] == "overlay":
continue
key = 'MOUNT_POINTS|{}'.format(row[6])
mountdict[key] = {}
mountdict[key]['Filesystem'] = row[0]
mountdict[key]['Type'] = row[1]
mountdict[key]['1K-blocks'] = row[2]
mountdict[key]['Used'] = row[3]
mountdict[key]['Available'] = row[4]
return mountdict

def create_mem_dict(self, mem_info):
mem_dict = {}
key = 'MEMORY_STATS|Physical'
mem_dict[key] = {}
mem_dict[key]['1K-blocks'] = mem_info["MemTotal"]
mem_dict[key]['Used'] = int(mem_info["MemTotal"]) - int(mem_info["MemFree"])

key = 'MEMORY_STATS|Virtual'
mem_dict[key] = {}
mem_dict[key]['1K-blocks'] = int(mem_info["MemTotal"]) + int(mem_info["SwapTotal"])
mem_dict[key]['Used'] = int(mem_info["MemTotal"]) + int(mem_info["SwapTotal"]) - int(mem_info["MemFree"]) - int(mem_info["SwapFree"])

key = 'MEMORY_STATS|Buffer'
mem_dict[key] = {}
mem_dict[key]['1K-blocks'] = mem_info["MemTotal"]
mem_dict[key]['Used'] = mem_info["Buffers"]

key = 'MEMORY_STATS|Cached'
mem_dict[key] = {}
mem_dict[key]['1K-blocks'] = mem_info["Cached"]
mem_dict[key]['Used'] = mem_info["Cached"]

key = 'MEMORY_STATS|Shared'
mem_dict[key] = {}
mem_dict[key]['1K-blocks'] = mem_info["Shmem"]
mem_dict[key]['Used'] = mem_info["Shmem"]

key = 'MEMORY_STATS|Swap'
mem_dict[key] = {}
mem_dict[key]['1K-blocks'] = mem_info["SwapTotal"]
mem_dict[key]['Used'] = int(mem_info["SwapTotal"]) - int(mem_info["SwapFree"])

return mem_dict

def update_dockerstats_command(self):
cmd = ["docker", "stats", "--no-stream", "-a"]
data = self.run_command(cmd)
Expand Down Expand Up @@ -223,6 +291,38 @@ class ProcDockerStats(daemon_base.DaemonBase):
update_value['enabled'] = str(enabled)
self.batch_update_state_db(fips_db_key, update_value)

def update_mountpointstats_command(self):
cmd = ["df", "-T"]
data = self.run_command(cmd)
if not data:
self.log_error("'{}' returned null output for filesystem".format(cmd))
return False
mountdata = self.format_mount_cmd_output(data)
if not mountdata:
self.log_error("formatting for filesystem output failed")
return False
self.state_db.delete_all_by_pattern('STATE_DB', 'MOUNT_POINTS|*')
for k1, v1 in mountdata.items():
for k2, v2 in v1.items():
self.update_state_db(k1, k2, v2)
return True

def update_memory_command(self):
cmd = ["cat", "/proc/meminfo"]
data = self.run_command(cmd)
if not data:
self.log_error("'{}' returned null output for memory stats".format(cmd))
return False
memdata = self.format_mem_output(data)
if not memdata:
self.log_error("formatting for memory stats failed")
return False
for k1, v1 in memdata.items():
for k2, v2 in v1.items():
self.update_state_db(k1, k2, v2)
return True


def update_state_db(self, key1, key2, value2):
self.state_db.set('STATE_DB', key1, key2, value2)

Expand All @@ -237,6 +337,7 @@ class ProcDockerStats(daemon_base.DaemonBase):
print("Must be root to run this daemon")
sys.exit(1)

counter = 1
while True:
self.update_dockerstats_command()
datetimeobj = datetime.now()
Expand All @@ -247,9 +348,18 @@ class ProcDockerStats(daemon_base.DaemonBase):
self.update_fipsstats_command()
self.update_state_db('FIPS_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj))

if counter == 5:
self.update_mountpointstats_command()
self.update_state_db('MOUNT_POINTS|LastUpdateTime', 'lastupdate', str(datetimeobj))
self.update_memory_command()
counter = 1

# Data need to be updated every 2 mins. hence adding delay of 120 seconds
time.sleep(120)

# Adding a counter to ensure mount points only updates every 10 mins, instead of 2 mins
counter += 1

self.log_info("Exiting ...")


Expand Down
108 changes: 108 additions & 0 deletions tests/procdockerstatsd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,111 @@ def test_update_fipsstats_command(self, mock_cmd):
pdstatsd.update_fipsstats_command()
assert pdstatsd.state_db.get('STATE_DB', 'FIPS_STATS|state', 'enforced') == "False"
assert pdstatsd.state_db.get('STATE_DB', 'FIPS_STATS|state', 'enabled') == "True"

def test_create_mount_dict_pass(self):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
mountpoint_dict = [["udev", "ext-4", "10000", "1000", "9000", "", "/dev"]]
mountpoint_dict_return = pdstatsd.create_mount_dict(mountpoint_dict)
key = 'MOUNT_POINTS|{}'.format(mountpoint_dict[0][6])

assert mountpoint_dict_return[key]['Filesystem'] == "udev"
assert mountpoint_dict_return[key]['Type'] == "ext-4"
assert mountpoint_dict_return[key]['1K-blocks'] == "10000"
assert mountpoint_dict_return[key]['Used'] == "1000"
assert mountpoint_dict_return[key]['Available'] == "9000"

def test_create_mount_dict_empty(self):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
mountpoint_dict = [["udev", "tmpfs", "10000", "1000", "9000", "", "/dev2"]]
mountpoint_dict_return = pdstatsd.create_mount_dict(mountpoint_dict)

assert len(mountpoint_dict_return) == 0

@patch.object(procdockerstatsd.ProcDockerStats, "create_mount_dict", return_value={"MOUNT_POINTS|/dev": {"Filesystem": "udev", "Type": "ext-4", "1K-blocks": "10000", "Used": "1000", "Available": "9000"}})
def test_update_mountpointstats_command(self, mock_create_mount_dict):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
ret = pdstatsd.update_mountpointstats_command()
mock_create_mount_dict.assert_called_once()
assert ret == True

@patch.object(procdockerstatsd.ProcDockerStats, "create_mount_dict", return_value={})
def test_update_mountpointstats_command_empty(self, mock_create_mount_dict):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
ret = pdstatsd.update_mountpointstats_command()
mock_create_mount_dict.assert_called_once()
assert not ret

@patch.object(procdockerstatsd.ProcDockerStats, "create_mount_dict", return_value={})
@patch.object(procdockerstatsd.ProcDockerStats, "run_command", return_value={})
def test_update_mountpointstats_command_fail(self, mock_create_mount_dict, mock_run_command):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
ret = pdstatsd.update_mountpointstats_command()

mock_create_mount_dict.assert_called_once()
assert ret == False

@patch.object(procdockerstatsd.ProcDockerStats, "create_mount_dict", return_value={"MOUNT_POINTS|/dev": {"Filesystem": "udev", "Type": "ext-4", "1K-blocks": "10000", "Used": "1000", "Available": "9000"}})
def test_format_mount_cmd_output(self, mock_create_mount_dict):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)

in_lines = "Filesystem Type 1K-blocks Used Available Use% Mounted on\nudev ext-4 10000 1000 9000 0% /dev"
ret = pdstatsd.format_mount_cmd_output(in_lines)
mock_create_mount_dict.assert_called_once()
assert ret

def test_create_mem_dict(self):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
mem_dict = {"MemTotal": "10000", "MemFree": "9000", "SwapTotal": "0", "SwapFree": "0", "Buffers": "123", "Cached": "456", "Shmem": "0"}
mem_dict_return = pdstatsd.create_mem_dict(mem_dict)

key = "MEMORY_STATS|Physical"
assert int(mem_dict_return[key]['Used']) == 1000
assert mem_dict_return[key]['1K-blocks'] == "10000"
key = "MEMORY_STATS|Swap"
assert int(mem_dict_return[key]['Used']) == 0
assert mem_dict_return[key]['1K-blocks'] == "0"

@patch.object(procdockerstatsd.ProcDockerStats, "create_mem_dict", return_value={
"MEMORY_STATS|Physical": {"1K-blocks": "10000", "Used": 1000},
"MEMORY_STATS|Virtual": {"1K-blocks": "10000", "Used": 1000},
"MEMORY_STATS|Buffer": {"1K-blocks": "10000", "Used": 123},
"MEMORY_STATS|Cached": {"1K-blocks": "456", "Used": 456},
"MEMORY_STATS|Shared": {"1K-blocks": "0", "Used": 0},
"MEMORY_STATS|Swap": {"1K-blocks": "0", "Used": 0}})
def test_format_mem_output(self, mock_create_mem_dict):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)

in_lines = "MemTotal: 10000 kB\n MemFree: 9000 kB\n SwapTotal: 0 kB\n SwapFree: 0\n Buffers: 123\n Cached: 456\n Shmem: 0"
ret = pdstatsd.format_mem_output(in_lines)
mock_create_mem_dict.assert_called_once()
assert ret

@patch.object(procdockerstatsd.ProcDockerStats, "create_mem_dict", return_value={
"MEMORY_STATS|Physical": {"1K-blocks": "10000", "Used": 1000},
"MEMORY_STATS|Virtual": {"1K-blocks": "10000", "Used": 1000},
"MEMORY_STATS|Buffer": {"1K-blocks": "10000", "Used": 123},
"MEMORY_STATS|Cached": {"1K-blocks": "456", "Used": 456},
"MEMORY_STATS|Shared": {"1K-blocks": "0", "Used": 0},
"MEMORY_STATS|Swap": {"1K-blocks": "0", "Used": 0}})
def test_update_memory_command(self, mock_create_mem_dict):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
ret = pdstatsd.update_memory_command()
mock_create_mem_dict.assert_called_once()
assert ret == True

@patch.object(procdockerstatsd.ProcDockerStats, "create_mem_dict", return_value={})
def test_update_memory_command_empty(self, mock_create_mem_dict):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
ret = pdstatsd.update_memory_command()
mock_create_mem_dict.assert_called_once()
assert not ret

@patch.object(procdockerstatsd.ProcDockerStats, "create_mem_dict", return_value={})
@patch.object(procdockerstatsd.ProcDockerStats, "run_command", return_value={})
def test_update_memory_command_fail(self, mock_create_mem_dict, mock_run_command):
pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER)
ret = pdstatsd.update_memory_command()

mock_create_mem_dict.assert_called_once()
assert ret == False

Loading