diff --git a/core/base_monitor.py b/core/base_monitor.py index 8d35ae4..a406331 100644 --- a/core/base_monitor.py +++ b/core/base_monitor.py @@ -110,7 +110,7 @@ class BaseMonitor: compatible recipes are used. Must be implmented by any child class.""" raise NotImplementedError - def _identify_new_rules(self, new_pattern:BasePattern=None, + def _identify_new_rules(self, new_pattern:BasePattern=None, new_recipe:BaseRecipe=None)->None: """Function to determine if a new rule can be created given a new pattern or recipe, in light of other existing patterns or recipes in diff --git a/patterns/network_event_pattern.py b/patterns/network_event_pattern.py index bc1f988..8a14749 100644 --- a/patterns/network_event_pattern.py +++ b/patterns/network_event_pattern.py @@ -1,10 +1,13 @@ import sys import socket +import threading +import tempfile +from time import time from typing import Any, Dict, List from meow_base.functionality.validation import valid_string, valid_dict -from meow_base.core.vars import VALID_RECIPE_NAME_CHARS, VALID_VARIABLE_NAME_CHARS +from meow_base.core.vars import VALID_RECIPE_NAME_CHARS, VALID_VARIABLE_NAME_CHARS, DEBUG_INFO from meow_base.core.base_recipe import BaseRecipe from meow_base.core.base_monitor import BaseMonitor from meow_base.core.base_pattern import BasePattern @@ -12,19 +15,17 @@ from meow_base.functionality.meow import create_event from meow_base.functionality.debug import setup_debugging, print_debug from meow_base.core.meow import EVENT_KEYS -# watchdog events +# network events EVENT_TYPE_NETWORK = "network" -NETWORK_BASE = "monitor_base" -NETWORK_HASH = "file_hash" +TRIGGERING_PORT = "triggering port" NETWORK_EVENT_KEYS = { - NETWORK_BASE: str, - NETWORK_HASH: str, + TRIGGERING_PORT: int, **EVENT_KEYS } -def create_network_event(temp_path:str, rule:Any, base:str, time:float, - hash:str, extras:Dict[Any,Any]={})->Dict[Any,Any]: +def create_network_event(temp_path:str, rule:Any, time:float, + port: int, extras:Dict[Any,Any]={})->Dict[Any,Any]: """Function to create a MEOW event dictionary.""" return create_event( EVENT_TYPE_NETWORK, @@ -32,39 +33,11 @@ def create_network_event(temp_path:str, rule:Any, base:str, time:float, rule, time, extras={ - **extras, - **{ - NETWORK_HASH: hash, - NETWORK_BASE: base - } + TRIGGERING_PORT: port, + **extras } ) -class Connector(): - def __init__(self, host: int, port: int) -> None: - self.host = host - self.port = port - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - # Only used for testing - def send(self, message: bytes) -> None: - self.socket.connect((self.host, self.port)) - self.socket.sendall(message) - self.socket.close() - - def receive(self, path:str, buff_size:int = 2048): - self.socket.bind((self.host, self.port)) - self.socket.listen() - conn, _ = self.socket.accept() - - with conn: - with open(path, "wb") as file_pointer: - while True: - data = conn.recv(buff_size) - if not data: - break - file_pointer.write(data) - class NetworkEventPattern(BasePattern): # The port to monitor triggering_port:int @@ -109,9 +82,6 @@ class NetworkMonitor(BaseMonitor): name:str="", print:Any=sys.stdout, logging:int=0) -> None: super().__init__(patterns, recipes, name=name) self._print_target, self.debug_level = setup_debugging(print, logging) - self.ports = set( - pattern.triggering_port for pattern in patterns.values() - ) if autostart: self.start() @@ -120,12 +90,48 @@ class NetworkMonitor(BaseMonitor): implemented by any child process. Depending on the nature of the monitor, this may wish to directly call apply_retroactive_rules before starting.""" - pass + self.ports = set( + pattern.triggering_port for pattern in self._patterns.values() + ) + self.listeners = [Listener("127.0.0.1",i,2048,self) for i in self.ports] + + for listener in self.listeners: + listener.start() + + def match(self, event)->None: + """Function to determine if a given event matches the current rules.""" + + self._rules_lock.acquire() + try: + for rule in self._rules.values(): + # Match event port against rule ports + hit = event["triggering port"] + + # If matched, the create a watchdog event + if hit: + meow_event = create_network_event( + event["tmp file"], + rule, + event["time stamp"], + event["triggering port"] + ) + print_debug(self._print_target, self.debug_level, + f"Event at {event['triggering port']} hit rule {rule.name}", + DEBUG_INFO) + # Send the event to the runner + self.send_event_to_runner(meow_event) + + except Exception as e: + self._rules_lock.release() + raise e + + self._rules_lock.release() def stop(self)->None: """Function to stop the monitor as an ongoing process/thread. Must be implemented by any child process""" - pass + for listener in self.listeners: + listener.stop() def _is_valid_recipes(self, recipes:Dict[str,BaseRecipe])->None: """Validation check for 'recipes' variable from main constructor. Is @@ -137,3 +143,59 @@ class NetworkMonitor(BaseMonitor): def _get_valid_recipe_types(self)->List[type]: return [BaseRecipe] + +class Listener(): + def __init__(self, host: int, port: int, buff_size: int, + monitor:NetworkMonitor) -> None: + self.host = host + self.port = port + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.settimeout(0.5) + self._stopped = False + self.buff_size = buff_size + self.monitor = monitor + + def start(self): + self._handle_thread = threading.Thread( + target=self.main_loop + ) + self._handle_thread.start() + + def main_loop(self): + self.socket.bind((self.host, self.port)) + self.socket.listen(1) + while not self._stopped: + try: + conn, _ = self.socket.accept() + except socket.timeout: + pass + except: + raise + else: + threading.Thread( + target=self.handle_event, + args=(conn,time(),) + ).start() + + + def handle_event(self, conn, time_stamp): + with conn: + with tempfile.NamedTemporaryFile("wb", delete=False) as tmp: + while True: + data = conn.recv(self.buff_size) + if not data: + break + tmp.write(data) + + tmp_name = tmp.name + + event = { + "triggering port": self.port, + "tmp file": tmp_name, + "time stamp": time_stamp + } + self.monitor.match(event) + + def stop(self): + self._stopped = True diff --git a/tests/test_patterns.py b/tests/test_patterns.py index 88ece01..dd573d7 100644 --- a/tests/test_patterns.py +++ b/tests/test_patterns.py @@ -1,7 +1,7 @@ import io import os import unittest -import threading +import socket import time from datetime import datetime @@ -17,15 +17,15 @@ from meow_base.patterns.file_event_pattern import FileEventPattern, \ WatchdogMonitor, _DEFAULT_MASK, WATCHDOG_HASH, WATCHDOG_BASE, \ EVENT_TYPE_WATCHDOG, WATCHDOG_EVENT_KEYS, create_watchdog_event from meow_base.patterns.network_event_pattern import NetworkEventPattern, \ - NetworkMonitor, NETWORK_HASH, NETWORK_BASE, EVENT_TYPE_NETWORK, \ - NETWORK_EVENT_KEYS, create_network_event, Connector + NetworkMonitor, TRIGGERING_PORT, EVENT_TYPE_NETWORK, \ + NETWORK_EVENT_KEYS, create_network_event from meow_base.recipes.jupyter_notebook_recipe import JupyterNotebookRecipe from meow_base.recipes.python_recipe import PythonRecipe from shared import BAREBONES_NOTEBOOK, TEST_MONITOR_BASE, \ COUNTING_PYTHON_SCRIPT, APPENDING_NOTEBOOK, setup, teardown -def patterns_equal(tester, pattern_one, pattern_two): +def file_patterns_equal(tester, pattern_one, pattern_two): tester.assertEqual(pattern_one.name, pattern_two.name) tester.assertEqual(pattern_one.recipe, pattern_two.recipe) tester.assertEqual(pattern_one.parameters, pattern_two.parameters) @@ -37,6 +37,14 @@ def patterns_equal(tester, pattern_one, pattern_two): tester.assertEqual(pattern_one.event_mask, pattern_two.event_mask) tester.assertEqual(pattern_one.sweep, pattern_two.sweep) +def network_patterns_equal(tester, pattern_one, pattern_two): + tester.assertEqual(pattern_one.name, pattern_two.name) + tester.assertEqual(pattern_one.recipe, pattern_two.recipe) + tester.assertEqual(pattern_one.parameters, pattern_two.parameters) + tester.assertEqual(pattern_one.outputs, pattern_two.outputs) + tester.assertEqual(pattern_one.triggering_port, + pattern_two.triggering_port) + def recipes_equal(tester, recipe_one, recipe_two): tester.assertEqual(recipe_one.name, recipe_two.name) @@ -630,9 +638,9 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 2) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) self.assertIn(pattern_two.name, patterns) - patterns_equal(self, patterns[pattern_two.name], pattern_two) + file_patterns_equal(self, patterns[pattern_two.name], pattern_two) # Test WatchdogMonitor add_pattern function def testMonitorAddPattern(self)->None: @@ -660,7 +668,7 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 1) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) wm.add_pattern(pattern_two) @@ -669,9 +677,9 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 2) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) self.assertIn(pattern_two.name, patterns) - patterns_equal(self, patterns[pattern_two.name], pattern_two) + file_patterns_equal(self, patterns[pattern_two.name], pattern_two) with self.assertRaises(KeyError): wm.add_pattern(pattern_two) @@ -681,9 +689,9 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 2) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) self.assertIn(pattern_two.name, patterns) - patterns_equal(self, patterns[pattern_two.name], pattern_two) + file_patterns_equal(self, patterns[pattern_two.name], pattern_two) # Test WatchdogMonitor update_patterns function def testMonitorUpdatePattern(self)->None: @@ -711,7 +719,7 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 1) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) pattern_one.recipe = "top_secret_recipe" @@ -742,7 +750,7 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 1) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) with self.assertRaises(KeyError): wm.update_pattern(pattern_two) @@ -751,7 +759,7 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 1) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) # Test WatchdogMonitor remove_patterns function def testMonitorRemovePattern(self)->None: @@ -779,7 +787,7 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 1) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) with self.assertRaises(KeyError): wm.remove_pattern(pattern_two) @@ -789,7 +797,7 @@ class WatchdogMonitorTests(unittest.TestCase): self.assertIsInstance(patterns, dict) self.assertEqual(len(patterns), 1) self.assertIn(pattern_one.name, patterns) - patterns_equal(self, patterns[pattern_one.name], pattern_one) + file_patterns_equal(self, patterns[pattern_one.name], pattern_one) wm.remove_pattern(pattern_one) @@ -1109,8 +1117,7 @@ class NetworkMonitorTests(unittest.TestCase): with self.assertRaises(TypeError): event = create_network_event("path", rule) - event = create_network_event( - "path", rule, "base", time(), "hash") + event = create_network_event("path", rule, time(), 8181) self.assertEqual(type(event), dict) self.assertEqual(len(event.keys()), len(NETWORK_EVENT_KEYS)) @@ -1120,15 +1127,13 @@ class NetworkMonitorTests(unittest.TestCase): self.assertEqual(event[EVENT_TYPE], EVENT_TYPE_NETWORK) self.assertEqual(event[EVENT_PATH], "path") self.assertEqual(event[EVENT_RULE], rule) - self.assertEqual(event[NETWORK_BASE], "base") - self.assertEqual(event[NETWORK_HASH], "hash") + self.assertEqual(event[TRIGGERING_PORT], 8181) event = create_network_event( "path2", rule, - "base", time(), - "hash", + 8182, extras={"a":1} ) @@ -1136,8 +1141,6 @@ class NetworkMonitorTests(unittest.TestCase): self.assertTrue(EVENT_TYPE in event.keys()) self.assertTrue(EVENT_PATH in event.keys()) self.assertTrue(EVENT_RULE in event.keys()) - self.assertTrue(NETWORK_BASE in event.keys()) - self.assertTrue(NETWORK_HASH in event.keys()) self.assertEqual(len(event.keys()), len(NETWORK_EVENT_KEYS)+1) for key, value in NETWORK_EVENT_KEYS.items(): self.assertTrue(key in event.keys()) @@ -1145,15 +1148,8 @@ class NetworkMonitorTests(unittest.TestCase): self.assertEqual(event[EVENT_TYPE], EVENT_TYPE_NETWORK) self.assertEqual(event[EVENT_PATH], "path2") self.assertEqual(event[EVENT_RULE], rule) + self.assertEqual(event[TRIGGERING_PORT], 8182) self.assertEqual(event["a"], 1) - self.assertEqual(event[NETWORK_BASE], "base") - self.assertEqual(event[NETWORK_HASH], "hash") - - - # Test NetworkMonitor created - def testNetworkMonitorMinimum(self)->None: - from_monitor = Pipe() - NetworkMonitor({}, {}, from_monitor[1]) # Test NetworkMonitor naming def testNetworkMonitorNaming(self)->None: @@ -1164,75 +1160,9 @@ class NetworkMonitorTests(unittest.TestCase): monitor = NetworkMonitor({}, {}) self.assertTrue(monitor.name.startswith("monitor_")) - # Test Connector data transfer when packet is smaller than buffer - def testConnector(self)->None: - message = b'test data' - localhost = "127.0.0.1" - port = 8181 - temp_path = f"{TEST_MONITOR_BASE}/result" - - receiver = Connector(localhost, port) - sender = Connector(localhost, port) - - receiver_thread = threading.Thread( - target=receiver.receive, - args=(temp_path,) - ) - - sender_thread = threading.Thread( - target=sender.send, - args=(message,) - ) - - receiver_thread.start() - sender_thread.start() - - sender_thread.join(5) - receiver_thread.join(5) - - self.assertFalse(sender_thread.is_alive()) - self.assertFalse(receiver_thread.is_alive()) - with open(temp_path,"rb") as file_pointer: - result_data = file_pointer.read() - - self.assertEqual(message, result_data) - - # Test Connector data transfer when packet is larger than buffer - def testConnectorBiggerThanBuffer(self)->None: - message = b'test data' - buffer_size = 4 - localhost = "127.0.0.1" - port = 8181 - temp_path = f"{TEST_MONITOR_BASE}/result" - - receiver = Connector(localhost, port) - sender = Connector(localhost, port) - - receiver_thread = threading.Thread( - target=receiver.receive, - args=(temp_path,buffer_size,) - ) - - sender_thread = threading.Thread( - target=sender.send, - args=(message,) - ) - - receiver_thread.start() - sender_thread.start() - - sender_thread.join(5) - receiver_thread.join(5) - - self.assertFalse(sender_thread.is_alive()) - self.assertFalse(receiver_thread.is_alive()) - with open(temp_path,"rb") as file_pointer: - result_data = file_pointer.read() - - self.assertEqual(message, result_data) - # Test NetworkMonitor identifies expected network events def testNetworkMonitorEventIdentification(self)->None: + localhost = "127.0.0.1" port = 8181 from_monitor_reader, from_monitor_writer = Pipe() @@ -1249,41 +1179,416 @@ class NetworkMonitorTests(unittest.TestCase): recipe.name: recipe, } - wm = NetworkMonitor(patterns, recipes) - wm.to_runner_event = from_monitor_writer + monitor = NetworkMonitor(patterns, recipes) + monitor.to_runner_event = from_monitor_writer - rules = wm.get_rules() + rules = monitor.get_rules() self.assertEqual(len(rules), 1) rule = rules[list(rules.keys())[0]] - # wm.start() + monitor.start() - # # open(os.path.join(TEST_MONITOR_BASE, "A"), "w") + sender = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sender.connect((localhost,port)) + sender.sendall(b'test') + sender.close() - # if from_monitor_reader.poll(3): - # message = from_monitor_reader.recv() + if from_monitor_reader.poll(3): + message = from_monitor_reader.recv() - # self.assertIsNotNone(message) - # event = message - # self.assertIsNotNone(event) - # self.assertEqual(type(event), dict) - # self.assertTrue(EVENT_TYPE in event.keys()) - # self.assertTrue(EVENT_PATH in event.keys()) - # self.assertTrue(WATCHDOG_BASE in event.keys()) - # self.assertTrue(EVENT_RULE in event.keys()) - # self.assertEqual(event[EVENT_TYPE], EVENT_TYPE_WATCHDOG) - # self.assertEqual(event[EVENT_PATH], - # os.path.join(TEST_MONITOR_BASE, "A")) - # self.assertEqual(event[WATCHDOG_BASE], TEST_MONITOR_BASE) - # self.assertEqual(event[EVENT_RULE].name, rule.name) + self.assertIsNotNone(message) + event = message + self.assertIsInstance(event, dict) + + self.assertTrue(EVENT_TYPE in event.keys()) + self.assertTrue(EVENT_PATH in event.keys()) + self.assertTrue(EVENT_RULE in event.keys()) + for key, value in NETWORK_EVENT_KEYS.items(): + self.assertTrue(key in event.keys()) + self.assertIsInstance(event[key], value) + self.assertEqual(event[EVENT_TYPE], EVENT_TYPE_NETWORK) + self.assertEqual(event[TRIGGERING_PORT], port) + self.assertEqual(event[EVENT_RULE].name, rule.name) + + monitor.stop() + + # Test NetworkMonitor get_patterns function + def testNetworkMonitorGetPatterns(self)->None: + pattern_one = NetworkEventPattern( + "pattern_one", + 8181, + "recipe_one", + parameters={}) + pattern_two = NetworkEventPattern( + "pattern_two", + 8182, + "recipe_two", + parameters={}) + + monitor = NetworkMonitor( + { + pattern_one.name: pattern_one, + pattern_two.name: pattern_two + }, + {} + ) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 2) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + self.assertIn(pattern_two.name, patterns) + network_patterns_equal(self, patterns[pattern_two.name], pattern_two) + + # Test NetworkMonitor add_pattern function + def testNetworkAddPattern(self)->None: + pattern_one = NetworkEventPattern( + "pattern_one", + 8181, + "recipe_one", + parameters={}) + pattern_two = NetworkEventPattern( + "pattern_two", + 8182, + "recipe_two", + parameters={}) + + monitor = NetworkMonitor( + {pattern_one.name: pattern_one}, + {} + ) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + + monitor.add_pattern(pattern_two) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 2) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + self.assertIn(pattern_two.name, patterns) + network_patterns_equal(self, patterns[pattern_two.name], pattern_two) + + with self.assertRaises(KeyError): + monitor.add_pattern(pattern_two) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 2) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + self.assertIn(pattern_two.name, patterns) + network_patterns_equal(self, patterns[pattern_two.name], pattern_two) + + # Test NetworkMonitor update_patterns function + def testMonitorUpdatePattern(self)->None: + pattern_one = NetworkEventPattern( + "pattern_one", + 8181, + "recipe_one", + parameters={}) + pattern_two = NetworkEventPattern( + "pattern_two", + 8182, + "recipe_two", + parameters={}) + + monitor = NetworkMonitor( + {pattern_one.name: pattern_one}, + {} + ) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + + pattern_one.recipe = "top_secret_recipe" + + patterns = monitor.get_patterns() + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + self.assertEqual(patterns[pattern_one.name].name, + pattern_one.name) + self.assertEqual(patterns[pattern_one.name].recipe, + "recipe_one") + self.assertEqual(patterns[pattern_one.name].parameters, + pattern_one.parameters) + self.assertEqual(patterns[pattern_one.name].outputs, + pattern_one.outputs) + self.assertEqual(patterns[pattern_one.name].triggering_port, + pattern_one.triggering_port) + + monitor.update_pattern(pattern_one) + + patterns = monitor.get_patterns() + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + + with self.assertRaises(KeyError): + monitor.update_pattern(pattern_two) + + patterns = monitor.get_patterns() + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + + # Test NetworkMonitor remove_patterns function + def testMonitorRemovePattern(self)->None: + pattern_one = NetworkEventPattern( + "pattern_one", + 8181, + "recipe_one", + parameters={}) + pattern_two = NetworkEventPattern( + "pattern_two", + 8182, + "recipe_two", + parameters={}) + + monitor = NetworkMonitor( + {pattern_one.name: pattern_one}, + {} + ) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + + with self.assertRaises(KeyError): + monitor.remove_pattern(pattern_two) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 1) + self.assertIn(pattern_one.name, patterns) + network_patterns_equal(self, patterns[pattern_one.name], pattern_one) + + monitor.remove_pattern(pattern_one) + + patterns = monitor.get_patterns() + + self.assertIsInstance(patterns, dict) + self.assertEqual(len(patterns), 0) + + # Test NetworkMonitor get_recipes function + def testMonitorGetRecipes(self)->None: + recipe_one = JupyterNotebookRecipe( + "recipe_one", BAREBONES_NOTEBOOK) + recipe_two = JupyterNotebookRecipe( + "recipe_two", BAREBONES_NOTEBOOK) + + monitor = NetworkMonitor( + {}, + { + recipe_one.name: recipe_one, + recipe_two.name: recipe_two + } + ) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 2) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + self.assertIn(recipe_two.name, recipes) + recipes_equal(self, recipes[recipe_two.name], recipe_two) + + # Test NetworkMonitor add_recipe function + def testMonitorAddRecipe(self)->None: + recipe_one = JupyterNotebookRecipe( + "recipe_one", BAREBONES_NOTEBOOK) + recipe_two = JupyterNotebookRecipe( + "recipe_two", BAREBONES_NOTEBOOK) + + monitor = NetworkMonitor( + {}, + { + recipe_one.name: recipe_one + } + ) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + + + monitor.add_recipe(recipe_two) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 2) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + self.assertIn(recipe_two.name, recipes) + recipes_equal(self, recipes[recipe_two.name], recipe_two) + + with self.assertRaises(KeyError): + monitor.add_recipe(recipe_two) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 2) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + self.assertIn(recipe_two.name, recipes) + recipes_equal(self, recipes[recipe_two.name], recipe_two) + + # Test NetworkMonitor update_recipe function + def testMonitorUpdateRecipe(self)->None: + recipe_one = JupyterNotebookRecipe( + "recipe_one", BAREBONES_NOTEBOOK) + recipe_two = JupyterNotebookRecipe( + "recipe_two", BAREBONES_NOTEBOOK) + + monitor = NetworkMonitor( + {}, + { + recipe_one.name: recipe_one + } + ) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + + recipe_one.source = "top_secret_source" + + recipes = monitor.get_recipes() + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + self.assertEqual(recipes[recipe_one.name].name, + recipe_one.name) + self.assertEqual(recipes[recipe_one.name].recipe, + recipe_one.recipe) + self.assertEqual(recipes[recipe_one.name].parameters, + recipe_one.parameters) + self.assertEqual(recipes[recipe_one.name].requirements, + recipe_one.requirements) + self.assertEqual(recipes[recipe_one.name].source, + "") + + monitor.update_recipe(recipe_one) + + recipes = monitor.get_recipes() + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + + with self.assertRaises(KeyError): + monitor.update_recipe(recipe_two) + + recipes = monitor.get_recipes() + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + + # Test NetworkMonitor remove_recipe function + def testMonitorRemoveRecipe(self)->None: + recipe_one = JupyterNotebookRecipe( + "recipe_one", BAREBONES_NOTEBOOK) + recipe_two = JupyterNotebookRecipe( + "recipe_two", BAREBONES_NOTEBOOK) + + monitor = NetworkMonitor( + {}, + { + recipe_one.name: recipe_one + } + ) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + + with self.assertRaises(KeyError): + monitor.remove_recipe(recipe_two) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 1) + self.assertIn(recipe_one.name, recipes) + recipes_equal(self, recipes[recipe_one.name], recipe_one) + + monitor.remove_recipe(recipe_one) + + recipes = monitor.get_recipes() + + self.assertIsInstance(recipes, dict) + self.assertEqual(len(recipes), 0) + + # Test NetworkMonitor get_rules function + def testMonitorGetRules(self)->None: + pattern_one = NetworkEventPattern( + "pattern_one", + 8181, + "recipe_one", + parameters={}) + pattern_two = NetworkEventPattern( + "pattern_two", + 8182, + "recipe_two", + parameters={}) + recipe_one = JupyterNotebookRecipe( + "recipe_one", BAREBONES_NOTEBOOK) + recipe_two = JupyterNotebookRecipe( + "recipe_two", BAREBONES_NOTEBOOK) + + patterns = { + pattern_one.name: pattern_one, + pattern_two.name: pattern_two, + } + recipes = { + recipe_one.name: recipe_one, + recipe_two.name: recipe_two, + } + + monitor = NetworkMonitor( + patterns, + recipes + ) + + rules = monitor.get_rules() + + self.assertIsInstance(rules, dict) + self.assertEqual(len(rules), 2) - # # open(os.path.join(TEST_MONITOR_BASE, "B"), "w") - # if from_monitor_reader.poll(3): - # new_message = from_monitor_reader.recv() - # else: - # new_message = None - # self.assertIsNone(new_message) - # wm.stop()