-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CMonitor : Added inotify support to cmonitor. #49
base: master
Are you sure you want to change the base?
Changes from all commits
1605fe5
40fe079
09e529a
4c5945f
0e12c06
69dcff4
702384f
7069bb1
150a863
d1d7246
ef2b049
9546155
c7c2854
c5a58a6
a5b3561
ae66b15
13adb06
959548a
ea020f0
6239457
74d8369
952a11a
2632082
b309721
c2de21e
b274e70
fb0c93e
42850c1
a2c359b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
#!/usr/bin/python3 | ||
|
||
# | ||
# cmonitor_watcher.py | ||
# | ||
# Author: Satyabrata Bharati | ||
# Created: April 2022 | ||
# | ||
import inotify.adapters | ||
import queue | ||
import os | ||
import time | ||
import logging | ||
from datetime import datetime | ||
|
||
exit_flag = False | ||
# ======================================================================================================= | ||
# CgroupWatcher : Basic inotify class | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this class looks good except for one thing: the timeout / sleep logic: it should not be included in this class.. it doesn't really belong here. The caller may want to sleep for whatever reason by watching cgroup should be fast and imply no sleeps. So please remove the "timeout" field. |
||
# ======================================================================================================= | ||
class CgroupWatcher: | ||
""" | ||
- Watch all files below a directory and notify an event for changes. | ||
- Retrieves all the process and extract the process name "/proc/<pid>/stat. | ||
- check the process name against the white-list given in the filter list. | ||
- store the events in Queue. | ||
""" | ||
|
||
def __init__(self, path, filter, timeout): | ||
"""Initialize CgroupWatcher | ||
Args: | ||
path: path to watch for events. | ||
filter: white-list against which the process-event is filtered. | ||
|
||
""" | ||
self.path = path | ||
self.filter = filter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. from the documentation it's not clear what is the format of self.filter honestly. Is it a list of strings? please document |
||
self.timeout = timeout | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as mentioned before, remove self.timeout |
||
self.myFileList = {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. self.myFileList can be removed as variable |
||
|
||
def __get_cgroup_version(self): | ||
""" | ||
Detect the cgroup version. | ||
""" | ||
proc_self_mount = "/proc/self/mounts" | ||
ncgroup_v1 = 0 | ||
ncgroup_v2 = 0 | ||
with open(proc_self_mount) as file: | ||
for line in file: | ||
row = line.split() | ||
fs_spec = row[0] | ||
fs_file = row[1] | ||
fs_vfstype = row[2] | ||
if (fs_spec == "cgroup" or fs_spec == "cgroup2") and fs_vfstype == "cgroup2": | ||
ncgroup_v2 += 1 | ||
else: | ||
ncgroup_v1 += 1 | ||
|
||
if ncgroup_v1 == 0 and ncgroup_v2 > 0: | ||
cgroup_versopn = "v2" | ||
return cgroup_version | ||
else: | ||
cgroup_version = "v1" | ||
return cgroup_version | ||
|
||
def __get_process_name(self, pid): | ||
"""Returns the process name for the process id. | ||
Args: | ||
pid: process id. | ||
|
||
Returns: | ||
The process name. | ||
|
||
""" | ||
cgroup_version = self.__get_cgroup_version() | ||
if cgroup_version == "v1": | ||
proc_filename = "/proc" + "/" + pid + "/stat" | ||
else: | ||
proc_filename = "/proc" + "/" + pid + "/cgroup.procs" | ||
with open(proc_filename) as file: | ||
for line in file: | ||
parts = line.split() | ||
process_name = parts[1].strip("()") | ||
return process_name | ||
|
||
def __get_pid_list(self, filename): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename to __get_pid_list_from_cgroup_procs |
||
"""Get the list of the process ids belong to a tasks file. | ||
Args: | ||
filename: the tasks file. | ||
|
||
Returns: | ||
The list of PIDs within the tasks file. | ||
|
||
""" | ||
list = [] | ||
with open(filename) as file: | ||
for line in file: | ||
list.append(line.strip()) | ||
return list | ||
|
||
def __get_list_of_files(self, dir): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename to __get_files_recursively |
||
"""Returns the list of the files created for the event within the watched dir. | ||
Args: | ||
filename: dir to be watched. | ||
|
||
Returns: | ||
The list of files created within the watched dir. | ||
|
||
""" | ||
listOfFiles = os.listdir(dir) | ||
allFiles = list() | ||
for entry in listOfFiles: | ||
fullpath = os.path.join(dir, entry) | ||
if os.path.isdir(fullpath): | ||
allFiles = allFiles + self.__get_list_of_files(fullpath) | ||
else: | ||
allFiles.append(fullpath) | ||
|
||
return allFiles | ||
|
||
def __process_task_files(self, dir): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename to __check_new_cgroup_against_filter(self, cgroup_dir) and provide an example of "cgroup_dir" that this function expects |
||
"""Process all the files for triggered-event within the watched dir. | ||
Finds the process Ids and filter out the process name against the | ||
provided white-list. If the process Id matches the whilte-listing | ||
process from command-line , it store and return the file anlog with the process-name. | ||
The process name later will be used to get the ip and port from the | ||
command-line for the specific process. | ||
Args: | ||
dir: dir to be watched. | ||
|
||
Returns: | ||
The file along with the process name which will be used to launch cmonitor. | ||
|
||
""" | ||
# time.sleep(20) | ||
time.sleep(self.timeout) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this sleep as mentioned before |
||
logging.info(f"watcher process file sleep: {self.timeout}") | ||
allFiles = self.__get_list_of_files(dir) | ||
for file in allFiles: | ||
if file.endswith("tasks"): | ||
list = self.__get_pid_list(file) | ||
if list: | ||
for pid in list: | ||
process_name = self.__get_process_name(pid) | ||
logging.info(f"processing task file: {file} with pid: {pid}, process name: {process_name}") | ||
match = self.__check_filter(process_name) | ||
if match is True: | ||
logging.info(f"Found match: {process_name}") | ||
self.myFileList = {file: process_name} | ||
return self.myFileList | ||
|
||
def __check_filter(self, process_name): | ||
"""Check process name against the whilte-list. | ||
Args: | ||
process_name: process name to be matched against the whilte-list from command-line. | ||
|
||
Returns: | ||
True if process_name matches with the white-list. | ||
|
||
""" | ||
for e in self.filter: | ||
if process_name in e: | ||
return True | ||
|
||
def inotify_events(self, queue): | ||
"""Main thread function for notifying events. | ||
Monitored events that match with the white-list provided will be stored in this queue. | ||
The events from this queue will be processed by cMonitorLauncher threading function to | ||
launch cMonitor with appropriate command input | ||
Args: | ||
queue: monitored events will be stored in this queue. | ||
|
||
Returns: | ||
|
||
""" | ||
logging.info(f"CgroupWatcher calling inotify_event") | ||
i = inotify.adapters.Inotify() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's a waste of resource to allocate the Inotify() class every time the inotify_events() method is invoked. this instance "i" should be allocated in the ctor and stored into "self.inotify_instance" member. |
||
i.add_watch(self.path) | ||
try: | ||
for event in i.event_gen(): | ||
if event is not None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment like "if event is None, it means the Inotify() system has no more events that need to be processed... give control back to the caller" |
||
if "IN_CREATE" in event[1]: | ||
(header, type_names, path, filename) = event | ||
logging.info(f"CgroupWatcher event triggered:{path},{filename}") | ||
dir = path + filename | ||
logging.info(f"CgroupWatcher event created:{filename}") | ||
fileList = self.__process_task_files(dir) | ||
if fileList: | ||
logging.info(f"CgroupWatcher event in Queue:{fileList}") | ||
queue.put(fileList) | ||
# global exit_flag | ||
if exit_flag is True: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this "if" |
||
logging.info(f"CgroupWatcher exit_flag {exit_flag}") | ||
exit(1) | ||
|
||
finally: | ||
i.remove_watch(path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this remove_watch() should be moved into the dtor of this class |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
ROOT_DIR:=$(shell readlink -f ../..) | ||
PYTHON_COMMON_CODE=$(ROOT_DIR)/tools/common-code | ||
|
||
run: | ||
export PYTHONPATH=$(PYTHON_COMMON_CODE) ; \ | ||
./cmonitor_launcher.py $(ARGS) | ||
|
||
test: | ||
cd tests && \ | ||
export PYTHONPATH=$(PYTHON_COMMON_CODE) && \ | ||
pytest --capture=no -vv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this flag should not be here... this class should not care about the whole app being requested to exit: it must just process the inotify events and fill a queue as fast as possible...that's it