From e75aa892691685e00e8a6e82b2b96f13ea82333f Mon Sep 17 00:00:00 2001 From: Mohit04tomar Date: Mon, 1 Jul 2024 14:30:58 +0530 Subject: [PATCH 01/91] Reverted DOCBRANCH to develop Signed-off-by: Mohit04tomar --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 8c8452ad1f..551a59b4c3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -178,4 +178,4 @@ # Pass Plugin DOCBRANCH argument in Makefile ; by default develop # NOTE: During release time we need to replace DOCBRANCH with actual released version -subprocess.run(["make generated DOCBRANCH='2.5.0RC'"], shell=True, check=True) +subprocess.run(["make generated DOCBRANCH='develop'"], shell=True, check=True) From a072897387fbe2f0eaba7b26f160f452dc0c32d7 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 2 Jul 2024 12:23:21 +0530 Subject: [PATCH 02/91] permission optional attribute added in configuration manager Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index 034cf429bc..1647feeffa 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -37,7 +37,7 @@ 'JSON', 'URL', 'enumeration', 'script', 'code', 'northTask', 'ACL', 'bucket', 'list', 'kvlist']) _optional_items = sorted(['readonly', 'order', 'length', 'maximum', 'minimum', 'rule', 'deprecated', 'displayName', - 'validity', 'mandatory', 'group', 'listSize', 'listName']) + 'validity', 'mandatory', 'group', 'listSize', 'listName', 'permission']) RESERVED_CATG = ['South', 'North', 'General', 'Advanced', 'Utilities', 'rest_api', 'Security', 'service', 'SCHEDULER', 'SMNTR', 'PURGE_READ', 'Notifications'] @@ -266,7 +266,7 @@ async def _validate_category_val(self, category_name, category_val, set_value_va optional_item_entries = {'readonly': 0, 'order': 0, 'length': 0, 'maximum': 0, 'minimum': 0, 'deprecated': 0, 'displayName': 0, 'rule': 0, 'validity': 0, 'mandatory': 0, - 'group': 0, 'listSize': 0, 'listName': 0} + 'group': 0, 'listSize': 0, 'listName': 0, 'permission': 0} expected_item_entries = {'description': 0, 'default': 0, 'type': 0} if require_entry_value: @@ -301,6 +301,19 @@ def get_entry_val(k): else: d = {entry_name: entry_val} expected_item_entries.update(d) + elif entry_name == "permission": + if not isinstance(entry_val, list): + raise ValueError( + 'For {} category, {} entry value must be in list for item name {}; got {}.' + ''.format(category_name, entry_name, item_name, type(entry_val))) + if not entry_val: + raise ValueError( + 'For {} category, {} entry value must not be an empty for item name ' + '{}.'.format(category_name, entry_name, item_name)) + else: + if not all(isinstance(ev, str) and ev != '' for ev in entry_val): + raise ValueError('For {} category, {} entry values must be in string and non-empty ' + 'for item name {}.'.format(category_name, entry_name, item_name)) else: if type(entry_val) is not str: raise TypeError('For {} category, entry value must be a string for item name {} and ' @@ -487,6 +500,19 @@ def get_entry_val(k): if entry_name in ('properties', 'options'): d = {entry_name: entry_val} expected_item_entries.update(d) + elif entry_name == "permission": + if not isinstance(entry_val, list): + raise ValueError( + 'For {} category, {} entry value must be in list for item name {}; got {}.' + ''.format(category_name, entry_name, item_name, type(entry_val))) + if not entry_val: + raise ValueError( + 'For {} category, {} entry value must not be an empty for item name ' + '{}.'.format(category_name, entry_name, item_name)) + else: + if not all(isinstance(ev, str) and ev != '' for ev in entry_val): + raise ValueError('For {} category, {} entry values must be in string and non-empty ' + 'for item name {}.'.format(category_name, entry_name, item_name)) else: if type(entry_val) is not str: raise TypeError('For {} category, entry value must be a string for item name {} and ' @@ -510,6 +536,19 @@ def get_entry_val(k): self._validate_type_value('float', entry_val)) is False: raise ValueError('For {} category, entry value must be an integer or float for item name ' '{}; got {}'.format(category_name, entry_name, type(entry_val))) + elif entry_name == "permission": + if not isinstance(entry_val, list): + raise ValueError( + 'For {} category, {} entry value must be in list for item name {}; got {}.' + ''.format(category_name, entry_name, item_name, type(entry_val))) + if not entry_val: + raise ValueError( + 'For {} category, {} entry value must not be an empty for item name ' + '{}.'.format(category_name, entry_name, item_name)) + else: + if not all(isinstance(ev, str) and ev != '' for ev in entry_val): + raise ValueError('For {} category, {} entry values must be in string and non-empty ' + 'for item name {}.'.format(category_name, entry_name, item_name)) elif entry_name in ('displayName', 'group', 'rule', 'validity', 'listName'): if not isinstance(entry_val, str): raise ValueError('For {} category, entry value must be string for item name {}; got {}' From b88a7542aa94f525c9d00755bd698b5d31954d0f Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 2 Jul 2024 12:24:39 +0530 Subject: [PATCH 03/91] rest_api and passwords core based categories updated with permission KV pair Signed-off-by: ashish-jabble --- python/fledge/services/core/server.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index 9cbafa157c..e923c43e88 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -241,7 +241,8 @@ class Server: 'options': ['mandatory', 'optional'], 'default': 'optional', 'displayName': 'Authentication', - 'order': '5' + 'order': '5', + 'permission': ['admin'] }, 'authMethod': { 'description': 'Authentication method', @@ -280,7 +281,8 @@ class Server: 'displayName': 'Idle User Session Disconnection (In Minutes)', 'order': '10', 'minimum': '1', - 'maximum': '1440' + 'maximum': '1440', + 'permission': ['admin'] } } @@ -522,7 +524,8 @@ async def password_config(cls): 'options': ['Any characters', 'Mixed case Alphabetic', 'Mixed case and numeric', 'Mixed case, numeric and special characters'], 'default': 'Any characters', 'displayName': 'Policy', - 'order': '1' + 'order': '1', + 'permission': ['admin'] }, 'length': { 'description': 'Minimum password length', @@ -531,14 +534,16 @@ async def password_config(cls): 'displayName': 'Minimum Length', 'minimum': '6', 'maximum': '80', - 'order': '2' + 'order': '2', + 'permission': ['admin'] }, 'expiration': { 'description': 'Number of days after which passwords must be changed', 'type': 'integer', 'default': '0', 'displayName': 'Expiry (in Days)', - 'order': '3' + 'order': '3', + 'permission': ['admin'] } } category = 'password' From 7dfeaf8f835929188c619e4fad990c34426099a7 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 2 Jul 2024 12:25:02 +0530 Subject: [PATCH 04/91] configuration tests updated with permission optional attribute Signed-off-by: ashish-jabble --- tests/system/python/api/test_configuration.py | 4 ++-- .../fledge/common/test_configuration_manager.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/system/python/api/test_configuration.py b/tests/system/python/api/test_configuration.py index 3303ea8b57..25e56efedf 100644 --- a/tests/system/python/api/test_configuration.py +++ b/tests/system/python/api/test_configuration.py @@ -167,12 +167,12 @@ def test_get_category(self, fledge_url): 'authCertificateName': {'displayName': 'Auth Certificate', 'description': 'Auth Certificate name', 'type': 'string', 'order': '7', 'value': 'ca', 'default': 'ca'}, 'certificateName': {'displayName': 'Certificate Name', 'description': 'Certificate file name', 'type': 'string', 'order': '4', 'value': 'fledge', 'default': 'fledge', 'validity': 'enableHttp=="false"'}, 'authProviders': {'displayName': 'Auth Providers', 'description': 'Authentication providers to use for the interface (JSON array object)', 'type': 'JSON', 'order': '9', 'value': '{"providers": ["username", "ldap"] }', 'default': '{"providers": ["username", "ldap"] }'}, - 'authentication': {'displayName': 'Authentication', 'description': 'API Call Authentication', 'type': 'enumeration', 'options': ['mandatory', 'optional'], 'order': '5', 'value': 'optional', 'default': 'optional'}, + 'authentication': {'displayName': 'Authentication', 'description': 'API Call Authentication', 'type': 'enumeration', 'options': ['mandatory', 'optional'], 'order': '5', 'value': 'optional', 'default': 'optional', 'permission': ['admin']}, 'authMethod': {'displayName': 'Authentication method', 'description': 'Authentication method', 'type': 'enumeration', 'options': ['any', 'password', 'certificate'], 'order': '6', 'value': 'any', 'default': 'any'}, 'httpPort': {'displayName': 'HTTP Port', 'description': 'Port to accept HTTP connections on', 'type': 'integer', 'order': '2', 'value': '8081', 'default': '8081'}, 'allowPing': {'displayName': 'Allow Ping', 'description': 'Allow access to ping, regardless of the authentication required and authentication header', 'type': 'boolean', 'order': '8', 'value': 'true', 'default': 'true'}, 'enableHttp': {'displayName': 'Enable HTTP', 'description': 'Enable HTTP (disable to use HTTPS)', 'type': 'boolean', 'order': '1', 'value': 'true', 'default': 'true'}, - 'disconnectIdleUserSession': {'description': 'Disconnect idle user session after certain period of inactivity', 'type': 'integer', 'default': '15', 'displayName': 'Idle User Session Disconnection (In Minutes)', 'order': '10', 'minimum': '1', 'maximum': '1440', 'value': '15'}} + 'disconnectIdleUserSession': {'description': 'Disconnect idle user session after certain period of inactivity', 'type': 'integer', 'default': '15', 'displayName': 'Idle User Session Disconnection (In Minutes)', 'order': '10', 'minimum': '1', 'maximum': '1440', 'value': '15', 'permission': ['admin']}} conn = http.client.HTTPConnection(fledge_url) conn.request("GET", '/fledge/category/rest_api') r = conn.getresponse() diff --git a/tests/unit/python/fledge/common/test_configuration_manager.py b/tests/unit/python/fledge/common/test_configuration_manager.py index 79e7bc869b..421e97815b 100644 --- a/tests/unit/python/fledge/common/test_configuration_manager.py +++ b/tests/unit/python/fledge/common/test_configuration_manager.py @@ -42,7 +42,7 @@ def test_supported_validate_type_strings(self): def test_supported_optional_items(self): expected_types = ['deprecated', 'displayName', 'group', 'length', 'mandatory', 'maximum', 'minimum', 'order', - 'readonly', 'rule', 'validity', 'listSize', 'listName'] + 'readonly', 'rule', 'validity', 'listSize', 'listName', 'permission'] assert len(expected_types) == len(_optional_items) assert sorted(expected_types) == _optional_items @@ -523,7 +523,18 @@ async def test__validate_category_val_config_entry_val_not_string(self): ({"description": "test description", "type": "enumeration", "default": "C", "options": ["A", "B"]}, ValueError, "For test category, entry value does not exist in options list for item name test_item_name and entry_name options; got C"), ({"description": 1, "type": "enumeration", "default": "A", "options": ["A", "B"]}, - TypeError, "For test category, entry value must be a string for item name test_item_name and entry name description; got ") + TypeError, "For test category, entry value must be a string for item name test_item_name and entry name description; got "), + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permission': ""}, + ValueError, + "For test category, permission entry value must be in list for item name test_item_name; got ."), + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permission': []}, + ValueError, "For test category, permission entry value must not be an empty for item name test_item_name."), + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permission': [""]}, + ValueError, + "For test category, permission entry values must be in string and non-empty for item name test_item_name."), + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], + 'permission': ["editor", 2]}, ValueError, + "For test category, permission entry values must be in string and non-empty for item name test_item_name.") ]) async def test__validate_category_val_enum_type_bad(self, config, exception_name, exception_msg): storage_client_mock = MagicMock(spec=StorageClientAsync) From dddc7ae6327179bfff0db1f96d266d49b439b61f Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 2 Jul 2024 14:34:40 +0530 Subject: [PATCH 05/91] permissions attribute separate handling in list, kvlist configuration type Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 16 ++++++- .../common/test_configuration_manager.py | 45 ++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index 1647feeffa..255a848be5 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -344,7 +344,7 @@ def get_entry_val(k): type(entry_val))) # Validate list type and mandatory items elif 'type' in item_val and get_entry_val("type") in ('list', 'kvlist'): - if entry_name not in ('properties', 'options') and not isinstance(entry_val, str): + if entry_name not in ('properties', 'options', 'permission') and not isinstance(entry_val, str): raise TypeError('For {} category, entry value must be a string for item name {} and ' 'entry name {}; got {}'.format(category_name, item_name, entry_name, type(entry_val))) @@ -361,6 +361,20 @@ def get_entry_val(k): raise ValueError('For {} category, listName cannot be empty for item name ' '{}'.format(category_name, item_name)) item_val['listName'] = list_name + elif "permission" in item_val: + permission = item_val['permission'] + if not isinstance(permission, list): + raise ValueError( + 'For {} category, permission entry value must be in list for item name {}; got {}.' + ''.format(category_name, item_name, type(permission))) + if not permission: + raise ValueError( + 'For {} category, permission entry value must not be an empty for item name {}.'.format( + category_name, item_name)) + else: + if not all(isinstance(ev, str) and ev != '' for ev in permission): + raise ValueError('For {} category, permission entry values must be in string and ' + 'non-empty for item name {}.'.format(category_name, item_name)) if entry_name == 'items': if entry_val not in ("string", "float", "integer", "object", "enumeration"): raise ValueError("For {} category, items value should either be in string, float, " diff --git a/tests/unit/python/fledge/common/test_configuration_manager.py b/tests/unit/python/fledge/common/test_configuration_manager.py index 421e97815b..a8a6d48190 100644 --- a/tests/unit/python/fledge/common/test_configuration_manager.py +++ b/tests/unit/python/fledge/common/test_configuration_manager.py @@ -691,6 +691,26 @@ async def test__validate_category_val_bucket_type_bad(self, config, exc_name, re ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", "properties": {"width": {"description": "", "default": "", "type": ""}}, "listName": ""}}, ValueError,"For {} category, listName cannot be empty for item name {}".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": ""}}, + ValueError, "For {} category, permission entry value must be in list for item name {}; got ." + "".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": []}}, + ValueError, "For {} category, permission entry value must not be an empty for item name {}.".format( + CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": [1]}}, + ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}.".format( + CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": ["a", 2]}}, + ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": ["", "A"]}}, + ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", "default": "A"}}, KeyError, "'For {} category, items KV pair must be required for item name {}.'".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", "default": "A", "items": []}}, TypeError, @@ -845,7 +865,23 @@ async def test__validate_category_val_bucket_type_bad(self, config, exc_name, re ({ITEM_NAME: {"description": "expression", "type": "kvlist", "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "listName": 2}}, TypeError, "For {} category, listName type must be a string for item name {}; got ".format( - CAT_NAME, ITEM_NAME)) + CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "expression", "type": "kvlist", + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": ""}}, + ValueError, "For {} category, permission entry value must be in list for item name {}; got ." + "".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "expression", "type": "kvlist", + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": []}}, + ValueError, "For {} category, permission entry value must not be an empty for item name {}.".format( + CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "expression", "type": "kvlist", + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": [""]}}, + ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "expression", "type": "kvlist", + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": [2]}}, + ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)), ]) async def test__validate_category_val_list_type_bad(self, config, exc_name, reason): storage_client_mock = MagicMock(spec=StorageClientAsync) @@ -871,6 +907,8 @@ async def test__validate_category_val_list_type_bad(self, config, exc_name, reas "default": "[\"var1\", \"var2\"]", "listSize": "2"}}, {"include": {"description": "A list of variables to include", "type": "list", "items": "string", "default": "[]", "listSize": "1"}}, + {"include": {"description": "A list of variables to include", "type": "list", "items": "string", + "default": "[]", "permission": ["user", "control"]}}, {"include": {"description": "A list of variables to include", "type": "list", "items": "integer", "default": "[\"10\", \"100\", \"200\", \"300\"]", "listSize": "4"}}, {"include": {"description": "A list of variables to include", "type": "list", "items": "object", @@ -900,7 +938,10 @@ async def test__validate_category_val_list_type_bad(self, config, exc_name, reas "properties": {"width": {"description": "Number of registers to read", "displayName": "Width", "type": "integer", "maximum": "4", "default": "1"}}}}, {"include": {"description": "A list of expressions and values ", "type": "kvlist", "default": - "{\"key1\": \"integer\", \"key2\": \"float\"}", "items": "enumeration", "options": ["integer", "float"]}} + "{\"key1\": \"integer\", \"key2\": \"float\"}", "items": "enumeration", "options": ["integer", "float"]}}, + {"include": {"description": "A list of expressions and values ", "type": "kvlist", "default": + "{\"key1\": \"integer\", \"key2\": \"float\"}", "items": "enumeration", "options": ["integer", "float"], + "permission": ["admin"]}} ]) async def test__validate_category_val_list_type_good(self, config): storage_client_mock = MagicMock(spec=StorageClientAsync) From aab3d1da1427f624f821beef6f76d119b77e7111 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 2 Jul 2024 15:17:04 +0530 Subject: [PATCH 06/91] configuration bucket type skipped tests enabled Signed-off-by: ashish-jabble --- .../common/test_configuration_manager.py | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/tests/unit/python/fledge/common/test_configuration_manager.py b/tests/unit/python/fledge/common/test_configuration_manager.py index a8a6d48190..4c0b1d2f9a 100644 --- a/tests/unit/python/fledge/common/test_configuration_manager.py +++ b/tests/unit/python/fledge/common/test_configuration_manager.py @@ -546,12 +546,42 @@ async def test__validate_category_val_enum_type_bad(self, config, exception_name assert excinfo.type is exception_name assert exception_msg == str(excinfo.value) - @pytest.mark.skip(reason="FOGL-8281") + #@pytest.mark.skip(reason="FOGL-8281") @pytest.mark.parametrize("config", [ - ({ITEM_NAME: {"description": "test description", "type": "bucket", "default": "A"}}), - ({ITEM_NAME: {"description": "test description", "type": "bucket", "default": "A", "properties": "{}"}}), + ({ITEM_NAME: {"description": "test description", "type": "bucket", + "default": "{'type': 'model', 'name': 'Person', 'version': '1.0', 'hardware': 'tpu'}", "properties": + {"constant": {"type": "model"}, "key": { + "name": {"description": "TFlite model name to use for inference", "type": "string", "default": "People", + "order": "1", "displayName": "TFlite model name"}}, "properties": { + "version": {"description": "Model version as stored in bucket", "type": "string", "default": "1.2", + "order": "2", "displayName": "Model version"}, "hardware": { + "description": "Inference hardware (\'tpu\' may be chosen only if available and configured properly)", + "type": "enumeration", "default": "cpu", "options": ["cpu", "tpu"], "order": "3", + "displayName": "Inference hardware"}}}}}), + ({ITEM_NAME: {"description": "test description", "type": "bucket", + "default": "{'type': 'model', 'name': 'Person', 'version': '1.0', 'hardware': 'tpu'}", + "properties": + {"constant": {"type": "model"}, "key": { + "name": {"description": "TFlite model name to use for inference", "type": "string", + "default": "People", + "order": "1", "displayName": "TFlite model name"}}, "properties": { + "version": {"description": "Model version as stored in bucket", "type": "string", + "default": "1.2", + "order": "2", "displayName": "Model version"}, "hardware": { + "description": "Inference hardware (\'tpu\' may be chosen only if available and configured properly)", + "type": "enumeration", "default": "cpu", "options": ["cpu", "tpu"], "order": "3", + "displayName": "Inference hardware"}}}}}), ({"item": {"description": "test description", "type": "string", "default": "A"}, - ITEM_NAME: {"description": "test description", "type": "bucket", "default": "A"}}), + ITEM_NAME: {"description": "test description", "type": "bucket", "default": + "{'type': 'model', 'name': 'People', 'version': '1.2', 'hardware': 'cpu'}", "properties": + {"constant": {"type": "model"}, "key": { + "name": {"description": "TFlite model name to use for inference", "type": "string", "default": "People", + "order": "1", "displayName": "TFlite model name"}}, "properties": { + "version": {"description": "Model version as stored in bucket", "type": "string", "default": "1.2", + "order": "2", "displayName": "Model version"}, "hardware": { + "description": "Inference hardware (\'tpu\' may be chosen only if available and configured properly)", + "type": "enumeration", "default": "cpu", "options": ["cpu", "tpu"], "order": "3", + "displayName": "Inference hardware"}}}}}) ]) async def test__validate_category_val_bucket_type_good(self, config): storage_client_mock = MagicMock(spec=StorageClientAsync) From 2af9931e0b637ca0fd2aff5ec5994a86c5499ee9 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 2 Jul 2024 16:03:56 +0530 Subject: [PATCH 07/91] C configuration support for permission attribute Signed-off-by: ashish-jabble --- C/common/config_category.cpp | 38 +++++++++++++++++++++++++++++- C/common/include/config_category.h | 2 ++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/C/common/config_category.cpp b/C/common/config_category.cpp index 397bfbceec..718b3bd2ee 100755 --- a/C/common/config_category.cpp +++ b/C/common/config_category.cpp @@ -1337,6 +1337,17 @@ ConfigCategory::CategoryItem::CategoryItem(const string& name, throw new runtime_error("ListName configuration item property is not a string"); } } + if (item.HasMember("permission")) + { + const Value& permission = item["permission"]; + if (permission.IsArray()) + { + for (SizeType i = 0; i < permission.Size(); i++) + { + m_permission.push_back(string(permission[i].GetString())); + } + } + } std::string m_typeUpperCase = m_type; for (auto & c: m_typeUpperCase) c = toupper(c); @@ -1624,6 +1635,10 @@ ConfigCategory::CategoryItem::CategoryItem(const CategoryItem& rhs) m_listSize = rhs.m_listSize; m_listItemType = rhs.m_listItemType; m_listName = rhs.m_listName; + for (auto it = rhs.m_permission.cbegin(); it != rhs.m_permission.cend(); it++) + { + m_permission.push_back(*it); + } } /** @@ -1748,6 +1763,17 @@ ostringstream convert; { convert << ", \"listName\" : \"" << m_listName << "\""; } + if (m_permission.size() > 0) + { + convert << "\"permission\" : [ "; + for (int i = 0; i < m_permission.size(); i++) + { + if (i > 0) + convert << ","; + convert << "\"" << m_permission[i] << "\""; + } + convert << "], "; + } } convert << " }"; @@ -1847,7 +1873,17 @@ ostringstream convert; { convert << ", \"listName\" : \"" << m_listName << "\""; } - + if (m_permission.size() > 0) + { + convert << ", \"permission\" : [ "; + for (int i = 0; i < m_permission.size(); i++) + { + if (i > 0) + convert << ","; + convert << "\"" << m_permission[i] << "\""; + } + convert << "]"; + } if (m_itemType == StringItem || m_itemType == EnumerationItem || diff --git a/C/common/include/config_category.h b/C/common/include/config_category.h index 2602286481..6aaf10ccea 100755 --- a/C/common/include/config_category.h +++ b/C/common/include/config_category.h @@ -194,6 +194,8 @@ class ConfigCategory { std::string m_listSize; std::string m_listItemType; std::string m_listName; + std::vector + m_permission; }; std::vector m_items; std::string m_name; From 1bd11cfda1a43bfd0537f50fbc52766a60dab206 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 3 Jul 2024 13:38:51 +0530 Subject: [PATCH 08/91] Update reject to the properties with permission property when the logged in user type is not given in the list of permission; also updated unit tests Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 53 ++++++++++++++++--- .../fledge/services/core/api/configuration.py | 52 +++++++----------- .../services/core/api/test_configuration.py | 27 ++++++++-- 3 files changed, 86 insertions(+), 46 deletions(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index 255a848be5..916983d404 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -804,26 +804,44 @@ async def _update_value_val(self, category_name, item_name, new_value_val): err_response = ex.error raise ValueError(err_response) - async def update_configuration_item_bulk(self, category_name, config_item_list): + async def update_configuration_item_bulk(self, category_name, config_item_list, request=None): """ Bulk update config items Args: category_name: category name config_item_list: dict containing config item values + request: request details to identify user info Returns: None """ - try: payload = {"updates": []} audit_details = {'category': category_name, 'items': {}} cat_info = await self.get_category_all_items(category_name) if cat_info is None: raise NameError("No such Category found for {}".format(category_name)) + """ Note: Update reject to the properties with permission property when the logged in user type is not + given in the list of permission. """ + if request is not None: + if hasattr(request, "user_is_admin"): + if not request.user_is_admin: + from fledge.services.core.user_model import User + roles = await User.Objects.get_roles() + user_role_name = [r['name'] for r in roles if request.user['role_id'] == r['id']] + if user_role_name: + user_role_name = user_role_name[0] + else: + raise ValueError("No role found for logged in user.") for item_name, new_val in config_item_list.items(): if item_name not in cat_info: raise KeyError('{} config item not found'.format(item_name)) + if request is not None: + if hasattr(request, "user_is_admin"): + if not request.user_is_admin: + if 'permission' in cat_info[item_name]: + if not (user_role_name in cat_info[item_name]['permission']): + raise Exception('Forbidden') # Evaluate new_val as per rule if defined if 'rule' in cat_info[item_name]: rule = cat_info[item_name]['rule'].replace("value", new_val) @@ -927,7 +945,8 @@ async def update_configuration_item_bulk(self, category_name, config_item_list): await audit.information('CONCH', audit_details) except Exception as ex: - _logger.exception(ex, 'Unable to bulk update config items') + if 'Forbidden' not in str(ex): + _logger.exception(ex, 'Unable to bulk update config items') raise try: @@ -1088,7 +1107,8 @@ async def get_category_item_value_entry(self, category_name, item_name): item_name) raise - async def set_category_item_value_entry(self, category_name, item_name, new_value_entry, script_file_path=""): + async def set_category_item_value_entry(self, category_name, item_name, new_value_entry, script_file_path="", + request=None): """Set the "value" entry of a given item within a given category. Keyword Arguments: @@ -1096,6 +1116,7 @@ async def set_category_item_value_entry(self, category_name, item_name, new_valu item_name -- name of item within the category whose "value" entry needs to be changed (required) new_value_entry -- new value entry to replace old value entry script_file_path -- Script file path for the config item whose type is script + request -- request details to identify user info Side Effects: An update to storage will not be issued if a new_value_entry is the same as the new_value_entry from storage. @@ -1128,6 +1149,21 @@ async def set_category_item_value_entry(self, category_name, item_name, new_valu .format(category_name, item_name)) if storage_value_entry == new_value_entry: return + """ Note: Update reject to the properties with permission property when the logged in user type is not + given in the list of permission. """ + if request is not None: + if hasattr(request, "user_is_admin"): + if not request.user_is_admin: + from fledge.services.core.user_model import User + roles = await User.Objects.get_roles() + user_role_name = [r['name'] for r in roles if request.user['role_id'] == r['id']] + if user_role_name: + user_role_name = user_role_name[0] + if 'permission' in storage_value_entry: + if not (user_role_name in storage_value_entry['permission']): + raise Exception('Forbidden') + else: + raise ValueError("No role found for logged in user.") # Special case for enumeration field type handling if storage_value_entry['type'] == 'enumeration': @@ -1175,10 +1211,11 @@ async def set_category_item_value_entry(self, category_name, item_name, new_valu self._cacheManager.cache[category_name]['value'][item_name]["file"] = script_file_path else: self._cacheManager.cache[category_name]['value'].update({item_name: cat_item['value']}) - except: - _logger.exception( - 'Unable to set item value entry based on category_name %s and item_name %s and value_item_entry %s', - category_name, item_name, new_value_entry) + except Exception as ex: + if 'Forbidden' not in str(ex): + _logger.exception( + 'Unable to set item value entry based on category_name %s and item_name %s and value_item_entry %s', + category_name, item_name, new_value_entry) raise try: await self._run_callbacks(category_name) diff --git a/python/fledge/services/core/api/configuration.py b/python/fledge/services/core/api/configuration.py index e75ee681bd..214a2ea493 100644 --- a/python/fledge/services/core/api/configuration.py +++ b/python/fledge/services/core/api/configuration.py @@ -245,23 +245,8 @@ async def set_configuration_item(request): """ category_name = request.match_info.get('category_name', None) config_item = request.match_info.get('config_item', None) - category_name = urllib.parse.unquote(category_name) if category_name is not None else None config_item = urllib.parse.unquote(config_item) if config_item is not None else None - """ - FIXME: FOGL-6774 We should handle in better way, there may be more cases. - GIVEN: config item 'authentication' And category 'rest_api' - WHEN: if non-admin user is trying to update - THEN: 403 Forbidden case - """ - if hasattr(request, "user"): - if request.user and (category_name == 'rest_api' and config_item == 'authentication'): - if not request.user_is_admin: - msg = "Admin role permissions required to change the {} value for category {}.".format( - config_item, category_name) - _logger.warning(msg) - raise web.HTTPForbidden(reason=msg, body=json.dumps({"message": msg})) - data = await request.json() cf_mgr = ConfigurationManager(connect.get_storage_async()) found_optional = {} @@ -293,14 +278,20 @@ async def set_configuration_item(request): if 'readonly' in storage_value_entry: if storage_value_entry['readonly'] == 'true': raise TypeError("Update not allowed for {} item_name as it has readonly attribute set".format(config_item)) - await cf_mgr.set_category_item_value_entry(category_name, config_item, value) + request_details = request if hasattr(request, "user") else None + await cf_mgr.set_category_item_value_entry(category_name, config_item, value, request=request_details) else: await cf_mgr.set_optional_value_entry(category_name, config_item, list(found_optional.keys())[0], list(found_optional.values())[0]) except ValueError as ex: raise web.HTTPNotFound(reason=ex) if not found_optional else web.HTTPBadRequest(reason=ex) except (TypeError, KeyError) as ex: raise web.HTTPBadRequest(reason=ex) - + except Exception as ex: + msg = str(ex) + if 'Forbidden' in msg: + msg = "Insufficient access privileges to change the value for category." + _logger.warning(msg) + raise web.HTTPForbidden(reason=msg, body=json.dumps({"message": msg})) category_item = await cf_mgr.get_category_item(category_name, config_item) if category_item is None: raise web.HTTPNotFound(reason="No detail found for the category_name: {} and config_item: {}".format(category_name, config_item)) @@ -325,20 +316,7 @@ async def update_configuration_item_bulk(request): data = await request.json() if not data: return web.HTTPBadRequest(reason='Nothing to update') - """ - FIXME: FOGL-6774 We should handle in better way, there may be more cases. - GIVEN: config item 'authentication' And category 'rest_api' - WHEN: if non-admin user is trying to update - THEN: 403 Forbidden case - """ - if hasattr(request, "user"): - config_items = [k for k, v in data.items() if k == 'authentication'] - if request.user and (category_name == 'rest_api' and config_items): - if not request.user_is_admin: - msg = "Admin role permissions required to change the authentication value for category {}.".format( - category_name) - _logger.warning(msg) - return web.HTTPForbidden(reason=msg, body=json.dumps({"message": msg})) + request_details = request if hasattr(request, "user") else None cf_mgr = ConfigurationManager(connect.get_storage_async()) try: is_core_mgt = request.is_core_mgt @@ -352,15 +330,21 @@ async def update_configuration_item_bulk(request): if storage_value_entry['readonly'] == 'true': raise TypeError( "Bulk update not allowed for {} item_name as it has readonly attribute set".format(item_name)) - await cf_mgr.update_configuration_item_bulk(category_name, data) + await cf_mgr.update_configuration_item_bulk(category_name, data, request_details) except (NameError, KeyError) as ex: raise web.HTTPNotFound(reason=ex) except (ValueError, TypeError) as ex: raise web.HTTPBadRequest(reason=ex) except Exception as ex: msg = str(ex) - _logger.error(ex, "Failed to bulk update {} category.".format(category_name)) - raise web.HTTPInternalServerError(reason=msg, body=json.dumps({"message": msg})) + if 'Forbidden' in msg: + if 'Forbidden' in msg: + msg = "Insufficient access privileges to change the value for category." + _logger.warning(msg) + raise web.HTTPForbidden(reason=msg, body=json.dumps({"message": msg})) + else: + _logger.error(ex, "Failed to bulk update {} category.".format(category_name)) + raise web.HTTPInternalServerError(reason=msg, body=json.dumps({"message": msg})) else: cat = await cf_mgr.get_category_all_items(category_name) try: diff --git a/tests/unit/python/fledge/services/core/api/test_configuration.py b/tests/unit/python/fledge/services/core/api/test_configuration.py index be069135e7..4f69024703 100644 --- a/tests/unit/python/fledge/services/core/api/test_configuration.py +++ b/tests/unit/python/fledge/services/core/api/test_configuration.py @@ -320,7 +320,13 @@ async def async_mock(return_value): args, kwargs = calls[1] assert category_name == args[0] assert item_name == args[1] - patch_set_entry.assert_called_once_with(category_name, item_name, payload['value']) + assert 1 == patch_set_entry.call_count + calls = patch_set_entry.call_args_list + args, _ = calls[0] + assert 3 == len(args) + assert category_name == args[0] + assert item_name == args[1] + assert payload['value'] == args[2] @pytest.mark.parametrize("payload, message", [ ({"valu": '8082'}, "Missing required value for http_port"), @@ -361,7 +367,8 @@ async def async_mock(return_value): resp = await client.put('/fledge/category/{}/{}'.format(category_name, item_name), data=json.dumps(payload)) assert 404 == resp.status - assert "No detail found for the category_name: {} and config_item: {}".format(category_name, item_name) == resp.reason + assert "No detail found for the category_name: {} and config_item: {}".format( + category_name, item_name) == resp.reason assert 2 == patch_get_cat_item.call_count calls = patch_get_cat_item.call_args_list args, kwargs = calls[0] @@ -370,7 +377,13 @@ async def async_mock(return_value): args, kwargs = calls[1] assert category_name == args[0] assert item_name == args[1] - patch_set_entry.assert_called_once_with(category_name, item_name, payload['value']) + assert 1 == patch_set_entry.call_count + calls = patch_set_entry.call_args_list + args, _ = calls[0] + assert 3 == len(args) + assert category_name == args[0] + assert item_name == args[1] + assert payload['value'] == args[2] @pytest.mark.parametrize("payload, optional_item, message", [ ({"value": '8082'}, "readonly", "Update not allowed for {} item_name as it has readonly attribute set") @@ -1074,7 +1087,13 @@ async def async_mock(return_value): json_response = json.loads(r) assert result == json_response patch_get_all_items.assert_called_once_with(category_name) - patch_update_bulk.assert_called_once_with(category_name, payload) + assert 1 == patch_update_bulk.call_count + calls = patch_update_bulk.call_args_list + args, _ = calls[0] + assert 3 == len(args) + assert category_name == args[0] + assert payload == args[1] + assert args[2] is not None assert 2 == patch_get_cat_item.call_count async def test_delete_configuration(self, client, category_name='rest_api'): From dfec7f26bf8685524f729b294a5f55cfa1783240 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 4 Jul 2024 13:33:08 +0530 Subject: [PATCH 09/91] GET users API endpoint restricted for non-admin users Signed-off-by: ashish-jabble --- python/fledge/common/web/middleware.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index e17c90e4b2..aa470b98fb 100644 --- a/python/fledge/common/web/middleware.py +++ b/python/fledge/common/web/middleware.py @@ -200,6 +200,10 @@ async def validate_requests(request): - All CRUD's privileges for control pipelines """ user_id = request.user['id'] + # Only URL's which are specific meant for Admin user + if not request.user_is_admin and request.method == 'GET': + if str(request.rel_url) == '/fledge/user': + raise web.HTTPForbidden # Normal/Editor user if int(request.user["role_id"]) == 2 and request.method != 'GET': # Special case: Allowed control entrypoint update request and handling of rejection in its handler @@ -216,7 +220,7 @@ async def validate_requests(request): elif int(request.user["role_id"]) == 4: if request.method == 'GET': supported_endpoints = ['/fledge/asset', '/fledge/ping', '/fledge/statistics', - '/fledge/user?id={}'.format(user_id), '/fledge/user/role'] + '/fledge/user?', '/fledge/user/role'] if not (str(request.rel_url).startswith(tuple(supported_endpoints) ) or str(request.rel_url).endswith('/fledge/service')): raise web.HTTPForbidden @@ -226,3 +230,4 @@ async def validate_requests(request): raise web.HTTPForbidden else: raise web.HTTPForbidden + From 33ada3f0344676ecf8808f074bc2bdb3376dc091 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 4 Jul 2024 13:33:40 +0530 Subject: [PATCH 10/91] Endpoints tests updated Signed-off-by: ashish-jabble --- ...est_endpoints_with_different_user_types.py | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/system/python/api/test_endpoints_with_different_user_types.py b/tests/system/python/api/test_endpoints_with_different_user_types.py index 9586f0a531..b82320cc1a 100644 --- a/tests/system/python/api/test_endpoints_with_different_user_types.py +++ b/tests/system/python/api/test_endpoints_with_different_user_types.py @@ -110,8 +110,12 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), - ("PUT", "/fledge/user/3/password", 500), ("GET", "/fledge/user/role", 200), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=3", 200), + ("GET", "/fledge/user?username={}".format(VIEW_USERNAME), 200), + ("GET", "/fledge/user?id={}&username={}".format(3, VIEW_USERNAME), 200), + ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 3), 200), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/3/password", 500), + ("GET", "/fledge/user/role", 200), # auth ("POST", "/fledge/login", 403), ("PUT", "/fledge/31/logout", 401), ("GET", "/fledge/auth/ott", 200), @@ -268,8 +272,12 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 403), ("GET", "/fledge/health/logging", 403), # user & roles - ("GET", "/fledge/user", 403), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), - ("PUT", "/fledge/user/4/password", 500), ("GET", "/fledge/user/role", 200), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=4", 200), + ("GET", "/fledge/user?username={}".format(DATA_VIEW_USERNAME), 200), + ("GET", "/fledge/user?id={}&username={}".format(4, DATA_VIEW_USERNAME), 200), + ("GET", "/fledge/user?username={}&id={}".format(DATA_VIEW_USERNAME, 4), 200), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/4/password", 500), + ("GET", "/fledge/user/role", 200), # auth ("POST", "/fledge/login", 403), ("PUT", "/fledge/31/logout", 401), ("GET", "/fledge/auth/ott", 403), @@ -424,8 +432,12 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 500), - ("PUT", "/fledge/user/3/password", 500), ("GET", "/fledge/user/role", 200), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=5", 200), + ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 200), + ("GET", "/fledge/user?id={}&username={}".format(5, CONTROL_USERNAME), 200), + ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 5), 200), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 500), ("PUT", "/fledge/user/3/password", 500), + ("GET", "/fledge/user/role", 200), # auth ("POST", "/fledge/login", 500), ("PUT", "/fledge/31/logout", 401), ("GET", "/fledge/auth/ott", 200), From 021bf2c823de806f2c483a95237e2cf3f75dbcee Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 4 Jul 2024 13:49:54 +0100 Subject: [PATCH 11/91] FOGL-8867 Add config cache size and log level documentation to tuning guide (#1410) * FOGL-8867 Add configuration category cache tuning to the documentation Signed-off-by: Mark Riddoch * FOGL-8867 Add documentation for setting configuration cache and log level Signed-off-by: Mark Riddoch * FOGL-8867 Add documentation for setting configuration cache and log level Signed-off-by: Mark Riddoch * Address review comments Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- docs/images/config_cache.jpg | Bin 0 -> 52642 bytes docs/images/core_log_level.jpg | Bin 0 -> 62847 bytes docs/tuning_fledge.rst | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 docs/images/config_cache.jpg create mode 100644 docs/images/core_log_level.jpg diff --git a/docs/images/config_cache.jpg b/docs/images/config_cache.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10f0d3458bb07814b74ec459db5c82b5e5901466 GIT binary patch literal 52642 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 z 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 Date: Fri, 5 Jul 2024 12:56:22 +0530 Subject: [PATCH 12/91] feedback changes Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 115 +++++++++--------- .../fledge/services/core/api/configuration.py | 6 +- python/fledge/services/core/server.py | 10 +- python/fledge/services/core/user_model.py | 7 ++ tests/system/python/api/test_configuration.py | 4 +- .../common/test_configuration_manager.py | 87 ++++++------- 6 files changed, 120 insertions(+), 109 deletions(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index 916983d404..31d47c5259 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -16,6 +16,7 @@ import collections import ast +import aiohttp.web_request from fledge.common.storage_client.payload_builder import PayloadBuilder from fledge.common.storage_client.storage_client import StorageClientAsync from fledge.common.storage_client.exceptions import StorageServerError @@ -37,7 +38,7 @@ 'JSON', 'URL', 'enumeration', 'script', 'code', 'northTask', 'ACL', 'bucket', 'list', 'kvlist']) _optional_items = sorted(['readonly', 'order', 'length', 'maximum', 'minimum', 'rule', 'deprecated', 'displayName', - 'validity', 'mandatory', 'group', 'listSize', 'listName', 'permission']) + 'validity', 'mandatory', 'group', 'listSize', 'listName', 'permissions']) RESERVED_CATG = ['South', 'North', 'General', 'Advanced', 'Utilities', 'rest_api', 'Security', 'service', 'SCHEDULER', 'SMNTR', 'PURGE_READ', 'Notifications'] @@ -266,7 +267,7 @@ async def _validate_category_val(self, category_name, category_val, set_value_va optional_item_entries = {'readonly': 0, 'order': 0, 'length': 0, 'maximum': 0, 'minimum': 0, 'deprecated': 0, 'displayName': 0, 'rule': 0, 'validity': 0, 'mandatory': 0, - 'group': 0, 'listSize': 0, 'listName': 0, 'permission': 0} + 'group': 0, 'listSize': 0, 'listName': 0, 'permissions': 0} expected_item_entries = {'description': 0, 'default': 0, 'type': 0} if require_entry_value: @@ -301,18 +302,18 @@ def get_entry_val(k): else: d = {entry_name: entry_val} expected_item_entries.update(d) - elif entry_name == "permission": + elif entry_name == "permissions": if not isinstance(entry_val, list): raise ValueError( - 'For {} category, {} entry value must be in list for item name {}; got {}.' + 'For {} category, {} entry value must be a list of string for item name {}; got {}.' ''.format(category_name, entry_name, item_name, type(entry_val))) if not entry_val: raise ValueError( - 'For {} category, {} entry value must not be an empty for item name ' + 'For {} category, {} entry value must not be empty for item name ' '{}.'.format(category_name, entry_name, item_name)) else: if not all(isinstance(ev, str) and ev != '' for ev in entry_val): - raise ValueError('For {} category, {} entry values must be in string and non-empty ' + raise ValueError('For {} category, {} entry values must be a string and non-empty ' 'for item name {}.'.format(category_name, entry_name, item_name)) else: if type(entry_val) is not str: @@ -344,7 +345,7 @@ def get_entry_val(k): type(entry_val))) # Validate list type and mandatory items elif 'type' in item_val and get_entry_val("type") in ('list', 'kvlist'): - if entry_name not in ('properties', 'options', 'permission') and not isinstance(entry_val, str): + if entry_name not in ('properties', 'options', 'permissions') and not isinstance(entry_val, str): raise TypeError('For {} category, entry value must be a string for item name {} and ' 'entry name {}; got {}'.format(category_name, item_name, entry_name, type(entry_val))) @@ -361,19 +362,19 @@ def get_entry_val(k): raise ValueError('For {} category, listName cannot be empty for item name ' '{}'.format(category_name, item_name)) item_val['listName'] = list_name - elif "permission" in item_val: - permission = item_val['permission'] - if not isinstance(permission, list): + elif "permissions" in item_val: + permissions = item_val['permissions'] + if not isinstance(permissions, list): raise ValueError( - 'For {} category, permission entry value must be in list for item name {}; got {}.' - ''.format(category_name, item_name, type(permission))) - if not permission: + 'For {} category, permissions entry value must be a list of string for item name {}; ' + 'got {}.'.format(category_name, item_name, type(permissions))) + if not permissions: raise ValueError( - 'For {} category, permission entry value must not be an empty for item name {}.'.format( + 'For {} category, permissions entry value must not be empty for item name {}.'.format( category_name, item_name)) else: - if not all(isinstance(ev, str) and ev != '' for ev in permission): - raise ValueError('For {} category, permission entry values must be in string and ' + if not all(isinstance(ev, str) and ev != '' for ev in permissions): + raise ValueError('For {} category, permissions entry values must be a string and ' 'non-empty for item name {}.'.format(category_name, item_name)) if entry_name == 'items': if entry_val not in ("string", "float", "integer", "object", "enumeration"): @@ -514,18 +515,18 @@ def get_entry_val(k): if entry_name in ('properties', 'options'): d = {entry_name: entry_val} expected_item_entries.update(d) - elif entry_name == "permission": + elif entry_name == "permissions": if not isinstance(entry_val, list): raise ValueError( - 'For {} category, {} entry value must be in list for item name {}; got {}.' + 'For {} category, {} entry value must be a list of string for item name {}; got {}.' ''.format(category_name, entry_name, item_name, type(entry_val))) if not entry_val: raise ValueError( - 'For {} category, {} entry value must not be an empty for item name ' + 'For {} category, {} entry value must not be empty for item name ' '{}.'.format(category_name, entry_name, item_name)) else: if not all(isinstance(ev, str) and ev != '' for ev in entry_val): - raise ValueError('For {} category, {} entry values must be in string and non-empty ' + raise ValueError('For {} category, {} entry values must be a string and non-empty ' 'for item name {}.'.format(category_name, entry_name, item_name)) else: if type(entry_val) is not str: @@ -550,18 +551,18 @@ def get_entry_val(k): self._validate_type_value('float', entry_val)) is False: raise ValueError('For {} category, entry value must be an integer or float for item name ' '{}; got {}'.format(category_name, entry_name, type(entry_val))) - elif entry_name == "permission": + elif entry_name == "permissions": if not isinstance(entry_val, list): raise ValueError( - 'For {} category, {} entry value must be in list for item name {}; got {}.' + 'For {} category, {} entry value must be a list of string for item name {}; got {}.' ''.format(category_name, entry_name, item_name, type(entry_val))) if not entry_val: raise ValueError( - 'For {} category, {} entry value must not be an empty for item name ' + 'For {} category, {} entry value must not be empty for item name ' '{}.'.format(category_name, entry_name, item_name)) else: if not all(isinstance(ev, str) and ev != '' for ev in entry_val): - raise ValueError('For {} category, {} entry values must be in string and non-empty ' + raise ValueError('For {} category, {} entry values must be a string and non-empty ' 'for item name {}.'.format(category_name, entry_name, item_name)) elif entry_name in ('displayName', 'group', 'rule', 'validity', 'listName'): if not isinstance(entry_val, str): @@ -821,27 +822,13 @@ async def update_configuration_item_bulk(self, category_name, config_item_list, cat_info = await self.get_category_all_items(category_name) if cat_info is None: raise NameError("No such Category found for {}".format(category_name)) - """ Note: Update reject to the properties with permission property when the logged in user type is not - given in the list of permission. """ - if request is not None: - if hasattr(request, "user_is_admin"): - if not request.user_is_admin: - from fledge.services.core.user_model import User - roles = await User.Objects.get_roles() - user_role_name = [r['name'] for r in roles if request.user['role_id'] == r['id']] - if user_role_name: - user_role_name = user_role_name[0] - else: - raise ValueError("No role found for logged in user.") + """ Note: Update reject to the properties with permissions property when the logged in user type is not + given in the list of permissions. """ + user_role_name = await self._check_updates_by_role(request) for item_name, new_val in config_item_list.items(): if item_name not in cat_info: raise KeyError('{} config item not found'.format(item_name)) - if request is not None: - if hasattr(request, "user_is_admin"): - if not request.user_is_admin: - if 'permission' in cat_info[item_name]: - if not (user_role_name in cat_info[item_name]['permission']): - raise Exception('Forbidden') + self._check_permissions(request, cat_info[item_name], user_role_name) # Evaluate new_val as per rule if defined if 'rule' in cat_info[item_name]: rule = cat_info[item_name]['rule'].replace("value", new_val) @@ -1149,22 +1136,11 @@ async def set_category_item_value_entry(self, category_name, item_name, new_valu .format(category_name, item_name)) if storage_value_entry == new_value_entry: return - """ Note: Update reject to the properties with permission property when the logged in user type is not - given in the list of permission. """ - if request is not None: - if hasattr(request, "user_is_admin"): - if not request.user_is_admin: - from fledge.services.core.user_model import User - roles = await User.Objects.get_roles() - user_role_name = [r['name'] for r in roles if request.user['role_id'] == r['id']] - if user_role_name: - user_role_name = user_role_name[0] - if 'permission' in storage_value_entry: - if not (user_role_name in storage_value_entry['permission']): - raise Exception('Forbidden') - else: - raise ValueError("No role found for logged in user.") - + """ Note: Update reject to the properties with permissions property when the logged in user type is not + given in the list of permissions. """ + user_role_name = await self._check_updates_by_role(request) + if user_role_name: + self._check_permissions(request, storage_value_entry, user_role_name) # Special case for enumeration field type handling if storage_value_entry['type'] == 'enumeration': if new_value_entry == '': @@ -2091,3 +2067,26 @@ def _handle_config_items(self, cat_name: str, cat_value: dict) -> None: if 'cacheSize' in cat_value: self._cacheManager.max_cache_size = int(cat_value['cacheSize']['value']) + async def _check_updates_by_role(self, request: aiohttp.web_request.Request) -> str: + async def get_role_name(): + from fledge.services.core.user_model import User + name = await User.Objects.get_role_name_by_id(request.user['role_id']) + if name is None: + raise ValueError("Requesting user's role is not matched with any existing roles.") + return name + + role_name = "" + if request is not None: + if hasattr(request, "user_is_admin"): + if not request.user_is_admin: + role_name = await get_role_name() + return role_name + + def _check_permissions(self, request: aiohttp.web_request.Request, cat_info: str, role_name: str) -> None: + if request is not None: + if hasattr(request, "user_is_admin"): + if not request.user_is_admin: + if 'permissions' in cat_info: + if not (role_name in cat_info['permissions']): + raise Exception('Forbidden') + diff --git a/python/fledge/services/core/api/configuration.py b/python/fledge/services/core/api/configuration.py index 214a2ea493..56beb77e55 100644 --- a/python/fledge/services/core/api/configuration.py +++ b/python/fledge/services/core/api/configuration.py @@ -289,7 +289,8 @@ async def set_configuration_item(request): except Exception as ex: msg = str(ex) if 'Forbidden' in msg: - msg = "Insufficient access privileges to change the value for category." + msg = "Insufficient access privileges to change the value for '{}' category and '{}' config item.".format( + category_name, config_item) _logger.warning(msg) raise web.HTTPForbidden(reason=msg, body=json.dumps({"message": msg})) category_item = await cf_mgr.get_category_item(category_name, config_item) @@ -339,7 +340,8 @@ async def update_configuration_item_bulk(request): msg = str(ex) if 'Forbidden' in msg: if 'Forbidden' in msg: - msg = "Insufficient access privileges to change the value for category." + msg = "Insufficient access privileges to change the value for given data for '{}' category.".format( + category_name) _logger.warning(msg) raise web.HTTPForbidden(reason=msg, body=json.dumps({"message": msg})) else: diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index e923c43e88..5f7b5db3c6 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -242,7 +242,7 @@ class Server: 'default': 'optional', 'displayName': 'Authentication', 'order': '5', - 'permission': ['admin'] + 'permissions': ['admin'] }, 'authMethod': { 'description': 'Authentication method', @@ -282,7 +282,7 @@ class Server: 'order': '10', 'minimum': '1', 'maximum': '1440', - 'permission': ['admin'] + 'permissions': ['admin'] } } @@ -525,7 +525,7 @@ async def password_config(cls): 'default': 'Any characters', 'displayName': 'Policy', 'order': '1', - 'permission': ['admin'] + 'permissions': ['admin'] }, 'length': { 'description': 'Minimum password length', @@ -535,7 +535,7 @@ async def password_config(cls): 'minimum': '6', 'maximum': '80', 'order': '2', - 'permission': ['admin'] + 'permissions': ['admin'] }, 'expiration': { 'description': 'Number of days after which passwords must be changed', @@ -543,7 +543,7 @@ async def password_config(cls): 'default': '0', 'displayName': 'Expiry (in Days)', 'order': '3', - 'permission': ['admin'] + 'permissions': ['admin'] } } category = 'password' diff --git a/python/fledge/services/core/user_model.py b/python/fledge/services/core/user_model.py index d13f0c9a0e..90636c8ef6 100644 --- a/python/fledge/services/core/user_model.py +++ b/python/fledge/services/core/user_model.py @@ -91,6 +91,13 @@ async def get_role_id_by_name(cls, name): result = await storage_client.query_tbl_with_payload('roles', payload) return result["rows"] + @classmethod + async def get_role_name_by_id(cls, rid): + storage_client = connect.get_storage_async() + payload = PayloadBuilder().SELECT("name").WHERE(['id', '=', rid]).LIMIT(1).payload() + result = await storage_client.query_tbl_with_payload('roles', payload) + return result['rows'][0] if result["rows"] else None + @classmethod async def create(cls, username, password, role_id, access_method='any', real_name='', description=''): """ diff --git a/tests/system/python/api/test_configuration.py b/tests/system/python/api/test_configuration.py index 25e56efedf..a7836a2dae 100644 --- a/tests/system/python/api/test_configuration.py +++ b/tests/system/python/api/test_configuration.py @@ -167,12 +167,12 @@ def test_get_category(self, fledge_url): 'authCertificateName': {'displayName': 'Auth Certificate', 'description': 'Auth Certificate name', 'type': 'string', 'order': '7', 'value': 'ca', 'default': 'ca'}, 'certificateName': {'displayName': 'Certificate Name', 'description': 'Certificate file name', 'type': 'string', 'order': '4', 'value': 'fledge', 'default': 'fledge', 'validity': 'enableHttp=="false"'}, 'authProviders': {'displayName': 'Auth Providers', 'description': 'Authentication providers to use for the interface (JSON array object)', 'type': 'JSON', 'order': '9', 'value': '{"providers": ["username", "ldap"] }', 'default': '{"providers": ["username", "ldap"] }'}, - 'authentication': {'displayName': 'Authentication', 'description': 'API Call Authentication', 'type': 'enumeration', 'options': ['mandatory', 'optional'], 'order': '5', 'value': 'optional', 'default': 'optional', 'permission': ['admin']}, + 'authentication': {'displayName': 'Authentication', 'description': 'API Call Authentication', 'type': 'enumeration', 'options': ['mandatory', 'optional'], 'order': '5', 'value': 'optional', 'default': 'optional', 'permissions': ['admin']}, 'authMethod': {'displayName': 'Authentication method', 'description': 'Authentication method', 'type': 'enumeration', 'options': ['any', 'password', 'certificate'], 'order': '6', 'value': 'any', 'default': 'any'}, 'httpPort': {'displayName': 'HTTP Port', 'description': 'Port to accept HTTP connections on', 'type': 'integer', 'order': '2', 'value': '8081', 'default': '8081'}, 'allowPing': {'displayName': 'Allow Ping', 'description': 'Allow access to ping, regardless of the authentication required and authentication header', 'type': 'boolean', 'order': '8', 'value': 'true', 'default': 'true'}, 'enableHttp': {'displayName': 'Enable HTTP', 'description': 'Enable HTTP (disable to use HTTPS)', 'type': 'boolean', 'order': '1', 'value': 'true', 'default': 'true'}, - 'disconnectIdleUserSession': {'description': 'Disconnect idle user session after certain period of inactivity', 'type': 'integer', 'default': '15', 'displayName': 'Idle User Session Disconnection (In Minutes)', 'order': '10', 'minimum': '1', 'maximum': '1440', 'value': '15', 'permission': ['admin']}} + 'disconnectIdleUserSession': {'description': 'Disconnect idle user session after certain period of inactivity', 'type': 'integer', 'default': '15', 'displayName': 'Idle User Session Disconnection (In Minutes)', 'order': '10', 'minimum': '1', 'maximum': '1440', 'value': '15', 'permissions': ['admin']}} conn = http.client.HTTPConnection(fledge_url) conn.request("GET", '/fledge/category/rest_api') r = conn.getresponse() diff --git a/tests/unit/python/fledge/common/test_configuration_manager.py b/tests/unit/python/fledge/common/test_configuration_manager.py index 4c0b1d2f9a..154f612a43 100644 --- a/tests/unit/python/fledge/common/test_configuration_manager.py +++ b/tests/unit/python/fledge/common/test_configuration_manager.py @@ -42,7 +42,7 @@ def test_supported_validate_type_strings(self): def test_supported_optional_items(self): expected_types = ['deprecated', 'displayName', 'group', 'length', 'mandatory', 'maximum', 'minimum', 'order', - 'readonly', 'rule', 'validity', 'listSize', 'listName', 'permission'] + 'readonly', 'rule', 'validity', 'listSize', 'listName', 'permissions'] assert len(expected_types) == len(_optional_items) assert sorted(expected_types) == _optional_items @@ -524,17 +524,17 @@ async def test__validate_category_val_config_entry_val_not_string(self): ValueError, "For test category, entry value does not exist in options list for item name test_item_name and entry_name options; got C"), ({"description": 1, "type": "enumeration", "default": "A", "options": ["A", "B"]}, TypeError, "For test category, entry value must be a string for item name test_item_name and entry name description; got "), - ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permission': ""}, + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permissions': ""}, + ValueError, "For test category, permissions entry value must be a list of string for item name test_item_name;" + " got ."), + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permissions': []}, + ValueError, "For test category, permissions entry value must not be empty for item name test_item_name."), + ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permissions': [""]}, ValueError, - "For test category, permission entry value must be in list for item name test_item_name; got ."), - ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permission': []}, - ValueError, "For test category, permission entry value must not be an empty for item name test_item_name."), - ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], 'permission': [""]}, - ValueError, - "For test category, permission entry values must be in string and non-empty for item name test_item_name."), + "For test category, permissions entry values must be a string and non-empty for item name test_item_name."), ({"description": "Test", "type": "enumeration", "default": "A", "options": ["A", "B"], - 'permission': ["editor", 2]}, ValueError, - "For test category, permission entry values must be in string and non-empty for item name test_item_name.") + 'permissions': ["editor", 2]}, ValueError, + "For test category, permissions entry values must be a string and non-empty for item name test_item_name.") ]) async def test__validate_category_val_enum_type_bad(self, config, exception_name, exception_msg): storage_client_mock = MagicMock(spec=StorageClientAsync) @@ -722,24 +722,24 @@ async def test__validate_category_val_bucket_type_bad(self, config, exc_name, re "properties": {"width": {"description": "", "default": "", "type": ""}}, "listName": ""}}, ValueError,"For {} category, listName cannot be empty for item name {}".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", - "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": ""}}, - ValueError, "For {} category, permission entry value must be in list for item name {}; got ." - "".format(CAT_NAME, ITEM_NAME)), + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permissions": ""}}, + ValueError, "For {} category, permissions entry value must be a list of string for item name {}; " + "got .".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", - "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": []}}, - ValueError, "For {} category, permission entry value must not be an empty for item name {}.".format( + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permissions": []}}, + ValueError, "For {} category, permissions entry value must not be empty for item name {}.".format( CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", - "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": [1]}}, - ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}.".format( + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permissions": [1]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}.".format( CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", - "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": ["a", 2]}}, - ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permissions": ["a", 2]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}." "".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "test", "type": "list", "default": "{\"key\": \"1.0\"}", "items": "object", - "properties": {"width": {"description": "", "default": "", "type": ""}}, "permission": ["", "A"]}}, - ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "properties": {"width": {"description": "", "default": "", "type": ""}}, "permissions": ["", "A"]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}." "".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", "default": "A"}}, KeyError, "'For {} category, items KV pair must be required for item name {}.'".format(CAT_NAME, ITEM_NAME)), @@ -897,20 +897,20 @@ async def test__validate_category_val_bucket_type_bad(self, config, exc_name, re TypeError, "For {} category, listName type must be a string for item name {}; got ".format( CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", - "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": ""}}, - ValueError, "For {} category, permission entry value must be in list for item name {}; got ." - "".format(CAT_NAME, ITEM_NAME)), + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permissions": ""}}, + ValueError, "For {} category, permissions entry value must be a list of string for item name {}; " + "got .".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", - "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": []}}, - ValueError, "For {} category, permission entry value must not be an empty for item name {}.".format( + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permissions": []}}, + ValueError, "For {} category, permissions entry value must not be empty for item name {}.".format( CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", - "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": [""]}}, - ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permissions": [""]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}." "".format(CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "expression", "type": "kvlist", - "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permission": [2]}}, - ValueError, "For {} category, permission entry values must be in string and non-empty for item name {}." + "default": "{\"key\": \"1.0\", \"key\": \"val2\"}", "items": "float", "permissions": [2]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}." "".format(CAT_NAME, ITEM_NAME)), ]) async def test__validate_category_val_list_type_bad(self, config, exc_name, reason): @@ -938,7 +938,7 @@ async def test__validate_category_val_list_type_bad(self, config, exc_name, reas {"include": {"description": "A list of variables to include", "type": "list", "items": "string", "default": "[]", "listSize": "1"}}, {"include": {"description": "A list of variables to include", "type": "list", "items": "string", - "default": "[]", "permission": ["user", "control"]}}, + "default": "[]", "permissions": ["user", "control"]}}, {"include": {"description": "A list of variables to include", "type": "list", "items": "integer", "default": "[\"10\", \"100\", \"200\", \"300\"]", "listSize": "4"}}, {"include": {"description": "A list of variables to include", "type": "list", "items": "object", @@ -971,7 +971,7 @@ async def test__validate_category_val_list_type_bad(self, config, exc_name, reas "{\"key1\": \"integer\", \"key2\": \"float\"}", "items": "enumeration", "options": ["integer", "float"]}}, {"include": {"description": "A list of expressions and values ", "type": "kvlist", "default": "{\"key1\": \"integer\", \"key2\": \"float\"}", "items": "enumeration", "options": ["integer", "float"], - "permission": ["admin"]}} + "permissions": ["admin"]}} ]) async def test__validate_category_val_list_type_good(self, config): storage_client_mock = MagicMock(spec=StorageClientAsync) @@ -3708,17 +3708,20 @@ async def async_mock(return_value): # Changed in version 3.8: patch() now returns an AsyncMock if the target is an async function. if sys.version_info.major == 3 and sys.version_info.minor >= 8: - _rv = await async_mock(cat_info) + rv1 = await async_mock(cat_info) + rv2 = await async_mock("") else: - _rv = asyncio.ensure_future(async_mock(cat_info)) - - with patch.object(c_mgr, 'get_category_all_items', return_value=_rv) as patch_get_all_items: - with patch.object(_logger, 'exception') as patch_log_exc: - with pytest.raises(Exception) as exc_info: - await c_mgr.update_configuration_item_bulk(category_name, config_item_list) - assert exc_type == exc_info.type - assert exc_msg == str(exc_info.value) - assert 1 == patch_log_exc.call_count + rv1 = asyncio.ensure_future(async_mock(cat_info)) + rv2 = asyncio.ensure_future(async_mock("")) + + with patch.object(c_mgr, 'get_category_all_items', return_value=rv1) as patch_get_all_items: + with patch.object(c_mgr, '_check_updates_by_role', return_value=rv2): + with patch.object(_logger, 'exception') as patch_log_exc: + with pytest.raises(Exception) as exc_info: + await c_mgr.update_configuration_item_bulk(category_name, config_item_list) + assert exc_type == exc_info.type + assert exc_msg == str(exc_info.value) + assert 1 == patch_log_exc.call_count patch_get_all_items.assert_called_once_with(category_name) async def test_update_configuration_item_bulk(self, category_name='rest_api'): From f4b297246af8b974d26a7f69b52cda804733e0de Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 5 Jul 2024 13:27:13 +0530 Subject: [PATCH 13/91] C changes reverted Signed-off-by: ashish-jabble --- C/common/config_category.cpp | 38 +----------------------------- C/common/include/config_category.h | 2 -- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/C/common/config_category.cpp b/C/common/config_category.cpp index 718b3bd2ee..397bfbceec 100755 --- a/C/common/config_category.cpp +++ b/C/common/config_category.cpp @@ -1337,17 +1337,6 @@ ConfigCategory::CategoryItem::CategoryItem(const string& name, throw new runtime_error("ListName configuration item property is not a string"); } } - if (item.HasMember("permission")) - { - const Value& permission = item["permission"]; - if (permission.IsArray()) - { - for (SizeType i = 0; i < permission.Size(); i++) - { - m_permission.push_back(string(permission[i].GetString())); - } - } - } std::string m_typeUpperCase = m_type; for (auto & c: m_typeUpperCase) c = toupper(c); @@ -1635,10 +1624,6 @@ ConfigCategory::CategoryItem::CategoryItem(const CategoryItem& rhs) m_listSize = rhs.m_listSize; m_listItemType = rhs.m_listItemType; m_listName = rhs.m_listName; - for (auto it = rhs.m_permission.cbegin(); it != rhs.m_permission.cend(); it++) - { - m_permission.push_back(*it); - } } /** @@ -1763,17 +1748,6 @@ ostringstream convert; { convert << ", \"listName\" : \"" << m_listName << "\""; } - if (m_permission.size() > 0) - { - convert << "\"permission\" : [ "; - for (int i = 0; i < m_permission.size(); i++) - { - if (i > 0) - convert << ","; - convert << "\"" << m_permission[i] << "\""; - } - convert << "], "; - } } convert << " }"; @@ -1873,17 +1847,7 @@ ostringstream convert; { convert << ", \"listName\" : \"" << m_listName << "\""; } - if (m_permission.size() > 0) - { - convert << ", \"permission\" : [ "; - for (int i = 0; i < m_permission.size(); i++) - { - if (i > 0) - convert << ","; - convert << "\"" << m_permission[i] << "\""; - } - convert << "]"; - } + if (m_itemType == StringItem || m_itemType == EnumerationItem || diff --git a/C/common/include/config_category.h b/C/common/include/config_category.h index 6aaf10ccea..2602286481 100755 --- a/C/common/include/config_category.h +++ b/C/common/include/config_category.h @@ -194,8 +194,6 @@ class ConfigCategory { std::string m_listSize; std::string m_listItemType; std::string m_listName; - std::vector - m_permission; }; std::vector m_items; std::string m_name; From 0f4b728958c0d0651511972eca04c8daa317557e Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 5 Jul 2024 18:50:53 +0530 Subject: [PATCH 14/91] GET user role endpoint restricted for non-admin users Signed-off-by: ashish-jabble --- python/fledge/services/core/api/auth.py | 1 + .../python/api/test_endpoints_with_different_user_types.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index f709367af4..298f5a396f 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -284,6 +284,7 @@ async def logout(request): return web.json_response({"logout": True}) +@has_permission("admin") async def get_roles(request): """ get roles diff --git a/tests/system/python/api/test_endpoints_with_different_user_types.py b/tests/system/python/api/test_endpoints_with_different_user_types.py index b82320cc1a..6542f8eaf0 100644 --- a/tests/system/python/api/test_endpoints_with_different_user_types.py +++ b/tests/system/python/api/test_endpoints_with_different_user_types.py @@ -115,7 +115,7 @@ def test_login(self, fledge_url, wait_time): ("GET", "/fledge/user?id={}&username={}".format(3, VIEW_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 3), 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/3/password", 500), - ("GET", "/fledge/user/role", 200), + ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 403), ("PUT", "/fledge/31/logout", 401), ("GET", "/fledge/auth/ott", 200), @@ -277,7 +277,7 @@ def test_login(self, fledge_url, wait_time): ("GET", "/fledge/user?id={}&username={}".format(4, DATA_VIEW_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(DATA_VIEW_USERNAME, 4), 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/4/password", 500), - ("GET", "/fledge/user/role", 200), + ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 403), ("PUT", "/fledge/31/logout", 401), ("GET", "/fledge/auth/ott", 403), @@ -437,7 +437,7 @@ def test_login(self, fledge_url, wait_time): ("GET", "/fledge/user?id={}&username={}".format(5, CONTROL_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 5), 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 500), ("PUT", "/fledge/user/3/password", 500), - ("GET", "/fledge/user/role", 200), + ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 500), ("PUT", "/fledge/31/logout", 401), ("GET", "/fledge/auth/ott", 200), From faa9db8261f0065945a5594ecd899b6c9f23f9d5 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 9 Jul 2024 11:52:49 +0530 Subject: [PATCH 15/91] Removed redundant text in display name of Configuration Cache Manager Signed-off-by: ashish-jabble --- docs/images/config_cache.jpg | Bin 52642 -> 75379 bytes python/fledge/services/core/server.py | 2 +- .../services/core/api/test_configuration.py | 4 ++-- .../fledge/services/core/test_server.py | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/images/config_cache.jpg b/docs/images/config_cache.jpg index 10f0d3458bb07814b74ec459db5c82b5e5901466..2c3b8b5afb32bdedd69c0bf1fc209285078deca8 100644 GIT binary patch literal 75379 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 8: diff --git a/tests/unit/python/fledge/services/core/test_server.py b/tests/unit/python/fledge/services/core/test_server.py index 6911270dad..4028c220dc 100644 --- a/tests/unit/python/fledge/services/core/test_server.py +++ b/tests/unit/python/fledge/services/core/test_server.py @@ -109,9 +109,9 @@ async def async_mock(return_value): storage_client_mock = MagicMock(spec=StorageClientAsync) Server._configuration_manager = ConfigurationManager(storage_client_mock) - value = {'cacheSize': {'description': 'To control the caching size of Core Configuration Manager', 'type': 'integer', - 'displayName': 'Configuration Manager Cache Size', 'default': '30', 'value': '30', 'order': '1', - 'minimum': '1', 'maximum': '1000'}} + value = {'cacheSize': {'description': 'To control the caching size of Core Configuration Manager', + 'type': 'integer', 'displayName': 'Cache Size', 'default': '30', 'value': '30', + 'order': '1', 'minimum': '1', 'maximum': '1000'}} rv = await async_mock(value) if sys.version_info.major == 3 and sys.version_info.minor >= 8 else ( asyncio.ensure_future(async_mock(value))) From 8fa25393a2a87ac132d5d076e4c4015318790d14 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 9 Jul 2024 12:13:14 +0530 Subject: [PATCH 16/91] text fixes in auth API Signed-off-by: ashish-jabble --- docs/securing_fledge.rst | 2 +- python/fledge/services/core/api/auth.py | 6 +++--- .../fledge/services/core/api/test_auth_mandatory.py | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index ca21cc03b4..bd4ff4f115 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -160,7 +160,7 @@ This popup can be used to change your password. On successfully changing your pa Password Policy --------------- -Fledge provides different policies to control the passwords. The following options are currently available: +Fledge provides different policies to control the user management passwords. The following options are currently available: +-------------------+ | |password_policy| | diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index f709367af4..d8e08cffda 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -583,7 +583,7 @@ async def update_password(request): raise web.HTTPBadRequest(reason=msg) if new_password and not isinstance(new_password, str): - err_msg = "New password should be in string format." + err_msg = "New password should not be provided in a non-string format." raise web.HTTPBadRequest(reason=err_msg, body=json.dumps({"message": err_msg})) error_msg = await validate_password(new_password) if error_msg: @@ -887,9 +887,9 @@ async def validate_password(password) -> str: min_chars = category['length']['value'] max_chars = category['length']['maximum'] if len(password) < int(min_chars): - message = "Password length is minimum of {} characters.".format(min_chars) + message = "Password should have a minimum length of {} characters.".format(min_chars) if len(password) > int(max_chars): - message = "Password length is maximum of {} characters.".format(max_chars) + message = "Password should have a maximum length of {} characters.".format(max_chars) if not message: has_lower = any(pwd.islower() for pwd in password) has_upper = any(pwd.isupper() for pwd in password) diff --git a/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py b/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py index cf1daa5062..f55ba21184 100644 --- a/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py +++ b/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py @@ -27,7 +27,7 @@ ADMIN_USER_HEADER = {'content-type': 'application/json', 'Authorization': 'admin_user_token'} NORMAL_USER_HEADER = {'content-type': 'application/json', 'Authorization': 'normal_user_token'} -PASSWORD_MIN_LENGTH_ERROR_MSG = "Password length is minimum of 6 characters." +PASSWORD_MIN_LENGTH_ERROR_MSG = "Password should have a minimum length of 6 characters." async def mock_coro(*args, **kwargs): return None if len(args) == 0 else args[0] @@ -530,7 +530,8 @@ async def test_update_user(self, client, mocker, payload, exp_result): ({"current_password": "F0gl@mp", "new_password": "F0gl@mp"}, "New password should not be the same as current password."), ({"current_password": "F0gl@mp", "new_password": "FL"}, PASSWORD_MIN_LENGTH_ERROR_MSG), - ({"current_password": "F0gl@mp", "new_password": 1}, "New password should be in string format.") + ({"current_password": "F0gl@mp", "new_password": 1}, + "New password should not be provided in a non-string format.") ]) async def test_update_password_with_bad_data(self, client, request_data, msg): uid = 2 @@ -1251,8 +1252,8 @@ async def async_mock(): assert "401: {}".format(expected) == actual @pytest.mark.parametrize("pwd, error_msg, policy", [ - ("pass", "Password length is minimum of 6 characters.", "Any characters"), - ("passwords", "Password length is maximum of 8 characters.", "Any characters"), + ("pass", "Password should have a minimum length of 6 characters.", "Any characters"), + ("passwords", "Password should have a maximum length of 8 characters.", "Any characters"), ("password", "Password must contain upper and lower case letters.", "Mixed case Alphabetic"), ("password", "Password must contain upper, lower case, uppercase and numeric values.", "Mixed case and numeric"), ("password", "Password must contain atleast one upper and lower case letter, numeric and special characters.", "Mixed case, numeric and special characters"), From ad9e672538a605dbbe8aa15e8de138827b78a536 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 9 Jul 2024 16:18:09 +0530 Subject: [PATCH 17/91] role name handling in user model Signed-off-by: ashish-jabble --- python/fledge/services/core/user_model.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/fledge/services/core/user_model.py b/python/fledge/services/core/user_model.py index 90636c8ef6..e8740eebe9 100644 --- a/python/fledge/services/core/user_model.py +++ b/python/fledge/services/core/user_model.py @@ -96,7 +96,12 @@ async def get_role_name_by_id(cls, rid): storage_client = connect.get_storage_async() payload = PayloadBuilder().SELECT("name").WHERE(['id', '=', rid]).LIMIT(1).payload() result = await storage_client.query_tbl_with_payload('roles', payload) - return result['rows'][0] if result["rows"] else None + name = None + if result["rows"]: + rows = result['rows'][0] + if 'name' in rows: + name = rows['name'] + return name @classmethod async def create(cls, username, password, role_id, access_method='any', real_name='', description=''): From 37646f5eccb89b57779894fd0d7915cb0ba8004b Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 9 Jul 2024 18:58:54 +0530 Subject: [PATCH 18/91] Group by service filteration added in GET monitors ALL call Signed-off-by: ashish-jabble --- .../services/core/api/performance_monitor.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/python/fledge/services/core/api/performance_monitor.py b/python/fledge/services/core/api/performance_monitor.py index 9a983b2fc3..d461f94f0b 100644 --- a/python/fledge/services/core/api/performance_monitor.py +++ b/python/fledge/services/core/api/performance_monitor.py @@ -44,14 +44,26 @@ async def get_all(request: web.Request) -> web.Response: monitors = await storage.query_tbl("monitors") counters = monitors["rows"] monitor = {} - response = {} for c in counters: val = {"average": c["average"], "maximum": c["maximum"], "minimum": c["minimum"], "samples": c["samples"], "timestamp": c["ts"], "service": c["service"]} monitor.setdefault(c['monitor'], []).append(val) monitors = [{'monitor': k, 'values': v} for k, v in monitor.items()] - response["monitors"] = monitors - return web.json_response(response) + # Group by service name + grouped_data = {} + for entry in monitors: + monitor_name = entry['monitor'] + values = entry['values'] + for value in values: + service = value.pop('service', None) + if service: + if service not in grouped_data: + grouped_data[service] = {} + if monitor_name not in grouped_data[service]: + grouped_data[service][monitor_name] = [] + # Group by monitor + grouped_data[service][monitor_name].append(value) + return web.json_response({"monitors": grouped_data}) async def get_by_service_name(request: web.Request) -> web.Response: From 7e8687ae5854e6558a3bbceb1ce385571ac8bb92 Mon Sep 17 00:00:00 2001 From: Ray Verhoeff Date: Tue, 9 Jul 2024 15:41:49 -0400 Subject: [PATCH 19/91] File and directory utilities Signed-off-by: Ray Verhoeff --- C/common/file_utils.cpp | 132 ++++++++++++++++++++++++++++++++++ C/common/include/file_utils.h | 16 +++++ 2 files changed, 148 insertions(+) create mode 100644 C/common/file_utils.cpp create mode 100644 C/common/include/file_utils.h diff --git a/C/common/file_utils.cpp b/C/common/file_utils.cpp new file mode 100644 index 0000000000..3aac13ec0f --- /dev/null +++ b/C/common/file_utils.cpp @@ -0,0 +1,132 @@ +/* + * Fledge utilities functions for handling files and directories + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Ray Verhoeff + */ + +#include +#include +#include +#include +#include +#include "file_utils.h" + +/** + * Callback for Linux file walk routine 'nftw' + * + * @param filePath File full path + * @param sb struct stat to hold file information + * @param typeflag File type flag: FTW_F = file, FTW_D = directory + * @param ftwbuf struct FTW to hold name offset and file depth + * @return Zero if successful + */ +static int fileDeleteCallback(const char *filePath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + return remove(filePath); +} + +/** + * Copy a file + * + * Implementation comes from https://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c + * + * @param to Full path of the destination file + * @param from Full path of the source file + * @return Zero if successful + */ +int copyFile(const char *to, const char *from) +{ + int fd_to, fd_from; + char buf[4096]; + ssize_t nread; + int saved_errno; + + fd_from = open(from, O_RDONLY); + if (fd_from < 0) + return -1; + + fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd_to < 0) + goto out_error; + + while (nread = read(fd_from, buf, sizeof buf), nread > 0) + { + char *out_ptr = buf; + ssize_t nwritten; + + do + { + nwritten = write(fd_to, out_ptr, nread); + + if (nwritten >= 0) + { + nread -= nwritten; + out_ptr += nwritten; + } + else if (errno != EINTR) + { + goto out_error; + } + } while (nread > 0); + } + + if (nread == 0) + { + if (close(fd_to) < 0) + { + fd_to = -1; + goto out_error; + } + close(fd_from); + + /* Success! */ + return 0; + } + +out_error: + saved_errno = errno; + + close(fd_from); + if (fd_to >= 0) + close(fd_to); + + errno = saved_errno; + return -1; +} + +/** + * Create a single directory. + * This routine cannot create a directory tree from a full path. + * This routine throws a std::runtime_error exception if the directory cannot be created. + * + * @param directoryName Full path of the directory to create + */ +void createDirectory(const std::string &directoryName) +{ + const char *path = directoryName.c_str(); + struct stat sb; + if (stat(path, &sb) != 0) + { + int retcode; + if ((retcode = mkdir(path, S_IRWXU | S_IRWXG)) != 0) + { + std::string exceptionMessage = "Unable to create directory " + directoryName + ": error: " + std::to_string(retcode); + throw std::runtime_error(exceptionMessage.c_str()); + } + } +} + +/** + * Remove a directory with all subdirectories and files + * + * @param path Full path of the directory + * @return Zero if successful + */ +int removeDirectory(const char *path) +{ + return nftw(path, fileDeleteCallback, 64, FTW_DEPTH | FTW_PHYS); +} diff --git a/C/common/include/file_utils.h b/C/common/include/file_utils.h new file mode 100644 index 0000000000..9a73bf1ecc --- /dev/null +++ b/C/common/include/file_utils.h @@ -0,0 +1,16 @@ +/* + * Fledge utilities functions for handling files and directories + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Ray Verhoeff + */ + +#pragma once +#include + +int copyFile(const char *to, const char *from); +void createDirectory(const std::string &directoryName); +int removeDirectory(const char *path); From c9d33cbe313ef0f5dfda6ac7a5e1e691e0c0dfff Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 10 Jul 2024 11:34:28 +0530 Subject: [PATCH 20/91] minor fixes Signed-off-by: ashish-jabble --- docs/securing_fledge.rst | 2 +- python/fledge/services/core/api/auth.py | 6 +++--- .../fledge/services/core/api/test_auth_mandatory.py | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index bd4ff4f115..642e887acd 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -160,7 +160,7 @@ This popup can be used to change your password. On successfully changing your pa Password Policy --------------- -Fledge provides different policies to control the user management passwords. The following options are currently available: +Fledge provides different policies to control the managed users password. The following options are currently available: +-------------------+ | |password_policy| | diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index d8e08cffda..d8f0507429 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -583,7 +583,7 @@ async def update_password(request): raise web.HTTPBadRequest(reason=msg) if new_password and not isinstance(new_password, str): - err_msg = "New password should not be provided in a non-string format." + err_msg = "New password should be a valid string." raise web.HTTPBadRequest(reason=err_msg, body=json.dumps({"message": err_msg})) error_msg = await validate_password(new_password) if error_msg: @@ -887,9 +887,9 @@ async def validate_password(password) -> str: min_chars = category['length']['value'] max_chars = category['length']['maximum'] if len(password) < int(min_chars): - message = "Password should have a minimum length of {} characters.".format(min_chars) + message = "Password should have minimum {} characters.".format(min_chars) if len(password) > int(max_chars): - message = "Password should have a maximum length of {} characters.".format(max_chars) + message = "Password should have maximum {} characters.".format(max_chars) if not message: has_lower = any(pwd.islower() for pwd in password) has_upper = any(pwd.isupper() for pwd in password) diff --git a/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py b/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py index f55ba21184..32d06afd11 100644 --- a/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py +++ b/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py @@ -27,7 +27,7 @@ ADMIN_USER_HEADER = {'content-type': 'application/json', 'Authorization': 'admin_user_token'} NORMAL_USER_HEADER = {'content-type': 'application/json', 'Authorization': 'normal_user_token'} -PASSWORD_MIN_LENGTH_ERROR_MSG = "Password should have a minimum length of 6 characters." +PASSWORD_MIN_LENGTH_ERROR_MSG = "Password should have minimum 6 characters." async def mock_coro(*args, **kwargs): return None if len(args) == 0 else args[0] @@ -530,8 +530,7 @@ async def test_update_user(self, client, mocker, payload, exp_result): ({"current_password": "F0gl@mp", "new_password": "F0gl@mp"}, "New password should not be the same as current password."), ({"current_password": "F0gl@mp", "new_password": "FL"}, PASSWORD_MIN_LENGTH_ERROR_MSG), - ({"current_password": "F0gl@mp", "new_password": 1}, - "New password should not be provided in a non-string format.") + ({"current_password": "F0gl@mp", "new_password": 1}, "New password should be a valid string.") ]) async def test_update_password_with_bad_data(self, client, request_data, msg): uid = 2 @@ -1252,8 +1251,8 @@ async def async_mock(): assert "401: {}".format(expected) == actual @pytest.mark.parametrize("pwd, error_msg, policy", [ - ("pass", "Password should have a minimum length of 6 characters.", "Any characters"), - ("passwords", "Password should have a maximum length of 8 characters.", "Any characters"), + ("pass", "Password should have minimum 6 characters.", "Any characters"), + ("passwords", "Password should have maximum 8 characters.", "Any characters"), ("password", "Password must contain upper and lower case letters.", "Mixed case Alphabetic"), ("password", "Password must contain upper, lower case, uppercase and numeric values.", "Mixed case and numeric"), ("password", "Password must contain atleast one upper and lower case letter, numeric and special characters.", "Mixed case, numeric and special characters"), From ddb3922218b42c403bcffa7d40a070c706dcf8db Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 10 Jul 2024 11:43:10 +0530 Subject: [PATCH 21/91] role tests added in user model Signed-off-by: ashish-jabble --- .../python/fledge/services/core/test_user_model.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit/python/fledge/services/core/test_user_model.py b/tests/unit/python/fledge/services/core/test_user_model.py index 3224871d86..9bec4cb712 100644 --- a/tests/unit/python/fledge/services/core/test_user_model.py +++ b/tests/unit/python/fledge/services/core/test_user_model.py @@ -87,6 +87,19 @@ async def test_get_role_id_by_name(self): assert actual == expected['rows'] query_tbl_patch.assert_called_once_with('roles', payload) + async def test_get_role_name_by_id(self): + expected = {'rows': [{'name': 'user'}], 'count': 1} + payload = '{"return": ["name"], "where": {"column": "id", "condition": "=", "value": 2}, "limit": 1}' + storage_client_mock = MagicMock(StorageClientAsync) + _rv = await mock_coro(expected) if sys.version_info.major == 3 and sys.version_info.minor >= 8 \ + else asyncio.ensure_future(mock_coro(expected)) + with patch.object(connect, 'get_storage_async', return_value=storage_client_mock): + with patch.object(storage_client_mock, 'query_tbl_with_payload', return_value=_rv + ) as query_tbl_patch: + actual = await User.Objects.get_role_name_by_id(2) + assert actual == expected['rows'][0]['name'] + query_tbl_patch.assert_called_once_with('roles', payload) + async def test_get_all(self): expected = {'rows': [], 'count': 0} storage_client_mock = MagicMock(StorageClientAsync) From 58699753c79b1fbf98bdf28ac7db7f2c2dd930b1 Mon Sep 17 00:00:00 2001 From: Ray Verhoeff Date: Wed, 10 Jul 2024 11:05:58 -0400 Subject: [PATCH 22/91] Fix comment Signed-off-by: Ray Verhoeff --- C/common/file_utils.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/C/common/file_utils.cpp b/C/common/file_utils.cpp index 3aac13ec0f..2237c86667 100644 --- a/C/common/file_utils.cpp +++ b/C/common/file_utils.cpp @@ -32,8 +32,6 @@ static int fileDeleteCallback(const char *filePath, const struct stat *sb, int t /** * Copy a file * - * Implementation comes from https://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c - * * @param to Full path of the destination file * @param from Full path of the source file * @return Zero if successful From 8a2c5d07768690e791d0c1b3598ac55176baaa3e Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 11 Jul 2024 11:46:47 +0100 Subject: [PATCH 23/91] FOGL-8840 Add C++ support for permissions property of configuration items (#1413) * Checkpoint Signed-off-by: Mark Riddoch * Add documentation Signed-off-by: Mark Riddoch * Configuration API tests updated Signed-off-by: ashish-jabble * Package based authentication tests updated Signed-off-by: ashish-jabble * Address review comments Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch Signed-off-by: ashish-jabble Co-authored-by: ashish-jabble --- C/common/config_category.cpp | 83 +++++++++++++++++++ C/common/include/config_category.h | 4 + .../02_writing_plugins.rst | 46 ++++++++++ python/fledge/services/core/server.py | 24 ++++-- tests/system/python/api/test_configuration.py | 16 ++-- .../python/packages/test_authentication.py | 3 +- 6 files changed, 158 insertions(+), 18 deletions(-) diff --git a/C/common/config_category.cpp b/C/common/config_category.cpp index 397bfbceec..46357ba542 100755 --- a/C/common/config_category.cpp +++ b/C/common/config_category.cpp @@ -872,6 +872,50 @@ vector ConfigCategory::getOptions(const string& name) const throw new ConfigItemNotFound(); } +/** + * Return the permissions of the configuration category item + * + * @param name The name of the configuration item to return + * @return vector The configuration item permissions + * @throws exception if the item does not exist in the category + */ +vector ConfigCategory::getPermissions(const string& name) const +{ + for (unsigned int i = 0; i < m_items.size(); i++) + { + if (name.compare(m_items[i]->m_name) == 0) + { + return m_items[i]->m_permissions; + } + } + throw new ConfigItemNotFound(); +} + +/** + * Return true if the user has permission to update the named item + * + * @param name The name of the configuration item to return + * @param rolename The name of the user role to test + * @return bool True if the named user can update the configuration item + * @throws exception if the item does not exist in the category + */ +bool ConfigCategory::hasPermission(const std::string& name, const std::string& rolename) const +{ + for (unsigned int i = 0; i < m_items.size(); i++) + { + if (name.compare(m_items[i]->m_name) == 0) + { + if (m_items[i]->m_permissions.empty()) + return true; + for (auto& perm : m_items[i]->m_permissions) + if (rolename.compare(perm) == 0) + return true; + return false; + } + } + throw new ConfigItemNotFound(); +} + /** * Return if the configuration item is a string item * @@ -1300,6 +1344,18 @@ ConfigCategory::CategoryItem::CategoryItem(const string& name, } } + if (item.HasMember("permissions")) + { + const Value& permissions = item["permissions"]; + if (permissions.IsArray()) + { + for (SizeType i = 0; i < permissions.Size(); i++) + { + m_permissions.push_back(string(permissions[i].GetString())); + } + } + } + if (item.HasMember("items")) { if (item["items"].IsString()) @@ -1624,6 +1680,10 @@ ConfigCategory::CategoryItem::CategoryItem(const CategoryItem& rhs) m_listSize = rhs.m_listSize; m_listItemType = rhs.m_listItemType; m_listName = rhs.m_listName; + for (auto it = rhs.m_permissions.cbegin(); it != rhs.m_permissions.cend(); it++) + { + m_permissions.push_back(*it); + } } /** @@ -1655,6 +1715,18 @@ ostringstream convert; convert << "], "; } + if (m_permissions.size() > 0) + { + convert << "\"permissions\" : [ "; + for (int i = 0; i < m_permissions.size(); i++) + { + if (i > 0) + convert << ","; + convert << "\"" << m_permissions[i] << "\""; + } + convert << "], "; + } + if (m_itemType == StringItem || m_itemType == BoolItem || m_itemType == EnumerationItem || @@ -1835,6 +1907,17 @@ ostringstream convert; } convert << "]"; } + if (m_permissions.size() > 0) + { + convert << ", \"permissions\" : [ "; + for (int i = 0; i < m_permissions.size(); i++) + { + if (i > 0) + convert << ","; + convert << "\"" << m_permissions[i] << "\""; + } + convert << "]"; + } if (!m_listSize.empty()) { convert << ", \"listSize\" : \"" << m_listSize << "\""; diff --git a/C/common/include/config_category.h b/C/common/include/config_category.h index 2602286481..8fb979190f 100755 --- a/C/common/include/config_category.h +++ b/C/common/include/config_category.h @@ -126,6 +126,8 @@ class ConfigCategory { void checkDefaultValuesOnly() const; std::string itemToJSON(const std::string& itemName) const; std::string to_string(const rapidjson::Value& v) const; + std::vector getPermissions(const std::string& name) const; + bool hasPermission(const std::string& name, const std::string& username) const; enum ItemAttribute { ORDER_ATTR, READONLY_ATTR, @@ -194,6 +196,8 @@ class ConfigCategory { std::string m_listSize; std::string m_listItemType; std::string m_listName; + std::vector + m_permissions; }; std::vector m_items; std::string m_name; diff --git a/docs/plugin_developers_guide/02_writing_plugins.rst b/docs/plugin_developers_guide/02_writing_plugins.rst index 5025a4ac2a..5cf5bb9584 100644 --- a/docs/plugin_developers_guide/02_writing_plugins.rst +++ b/docs/plugin_developers_guide/02_writing_plugins.rst @@ -636,6 +636,8 @@ Properties - The current value of the configuration item. This is not included when defining a set of default configuration in, for example, a plugin. * - properties - A set of items that are used in list and kvlist type items to create a list of groups of configuration items. + * - permissions + - An array of user roles that are allowed to update this configuration item. If not given then the configuration item can be updated by any user. If the permissions property is included in a configuration item the array must have at least one entry. Of the above properties of a configuration item *type*, *default* and *description* are mandatory, all others are optional. @@ -724,3 +726,47 @@ There is also a convenience function that can be used if you not want to define AuditLogger::auditLog("NHAVL", "INFORMATION"); +Permissions +~~~~~~~~~~~ + +The permissions property is used to control the update of configuration items within a category. If Fledge has been configured such that it requires authentication in order to connect to the REST API, or by extension the Fledge GUI, then when a user attempts to update a category which contains an item with the permissions property set, the user role will be fetched and compared to the list of roles able to update the item. Items within the category that do not have the permissions property will not be affected. + +The REST API category is an example of using the permissions property to restrict the items in the category that non-admin role users can change. Below we show one of the configuration items that is restricted, the one that controls the requirement to authenticate when connecting to Fledge. + +.. code-block:: JSON + + "authentication": { + "description": "API Call Authentication", + "type": "enumeration", + "options": [ + "mandatory", + "optional" + ], + "default": "optional", + "displayName": "Authentication", + "order": "5", + "permissions": [ + "admin" + ], + "value": "optional" + } + +The use of the permissions property with the single role of admin means that only admin users can change this setting. If we wished to allow more than admin users we can add another role. For example + +.. code-block:: JSON + + "logLevel": { + "description": "Minimum logging level reported for Core server", + "type": "enumeration", + "displayName": "Minimum Log Level", + "options": ["debug", "info", "warning", "error", "critical"], + "default": "warning", + "order": "1", + "permissions": ["admin", "control"] + } + +In this case users with the role admin or control are allowed to alter the configuration item. + +.. note:: + + User created with the role of view configuration or view data are unable to alter any configuration items regardless of the permissions properties on those items. diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index 4e8528ee84..2eab1580e2 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -210,14 +210,16 @@ class Server: 'type': 'boolean', 'default': 'true', 'displayName': 'Enable HTTP', - 'order': '1' + 'order': '1', + 'permissions': ['admin'] }, 'httpPort': { 'description': 'Port to accept HTTP connections on', 'type': 'integer', 'default': '8081', 'displayName': 'HTTP Port', - 'order': '2' + 'order': '2', + 'permissions': ['admin'] }, 'httpsPort': { 'description': 'Port to accept HTTPS connections on', @@ -225,7 +227,8 @@ class Server: 'default': '1995', 'displayName': 'HTTPS Port', 'order': '3', - 'validity': 'enableHttp=="false"' + 'validity': 'enableHttp=="false"', + 'permissions': ['admin'] }, 'certificateName': { 'description': 'Certificate file name', @@ -233,7 +236,8 @@ class Server: 'default': 'fledge', 'displayName': 'Certificate Name', 'order': '4', - 'validity': 'enableHttp=="false"' + 'validity': 'enableHttp=="false"', + 'permissions': ['admin'] }, 'authentication': { 'description': 'API Call Authentication', @@ -250,14 +254,16 @@ class Server: 'options': ["any", "password", "certificate"], 'default': 'any', 'displayName': 'Authentication method', - 'order': '6' + 'order': '6', + 'permissions': ['admin'] }, 'authCertificateName': { 'description': 'Auth Certificate name', 'type': 'string', 'default': 'ca', 'displayName': 'Auth Certificate', - 'order': '7' + 'order': '7', + 'permissions': ['admin'] }, 'allowPing': { 'description': 'Allow access to ping, regardless of the authentication required and' @@ -265,14 +271,16 @@ class Server: 'type': 'boolean', 'default': 'true', 'displayName': 'Allow Ping', - 'order': '8' + 'order': '8', + 'permissions': ['admin'] }, 'authProviders': { 'description': 'Authentication providers to use for the interface (JSON array object)', 'type': 'JSON', 'default': '{"providers": ["username", "ldap"] }', 'displayName': 'Auth Providers', - 'order': '9' + 'order': '9', + 'permissions': ['admin'] }, 'disconnectIdleUserSession': { 'description': 'Disconnect idle user session after certain period of inactivity', diff --git a/tests/system/python/api/test_configuration.py b/tests/system/python/api/test_configuration.py index a7836a2dae..90e08b8ca1 100644 --- a/tests/system/python/api/test_configuration.py +++ b/tests/system/python/api/test_configuration.py @@ -163,15 +163,15 @@ def test_default(self, fledge_url, reset_and_start_fledge, wait_time, storage_pl assert expected_with_utilities == jdoc["categories"] def test_get_category(self, fledge_url): - expected = {'httpsPort': {'displayName': 'HTTPS Port', 'description': 'Port to accept HTTPS connections on', 'type': 'integer', 'order': '3', 'value': '1995', 'default': '1995', 'validity': 'enableHttp=="false"'}, - 'authCertificateName': {'displayName': 'Auth Certificate', 'description': 'Auth Certificate name', 'type': 'string', 'order': '7', 'value': 'ca', 'default': 'ca'}, - 'certificateName': {'displayName': 'Certificate Name', 'description': 'Certificate file name', 'type': 'string', 'order': '4', 'value': 'fledge', 'default': 'fledge', 'validity': 'enableHttp=="false"'}, - 'authProviders': {'displayName': 'Auth Providers', 'description': 'Authentication providers to use for the interface (JSON array object)', 'type': 'JSON', 'order': '9', 'value': '{"providers": ["username", "ldap"] }', 'default': '{"providers": ["username", "ldap"] }'}, + expected = {'httpsPort': {'displayName': 'HTTPS Port', 'description': 'Port to accept HTTPS connections on', 'type': 'integer', 'order': '3', 'value': '1995', 'default': '1995', 'validity': 'enableHttp=="false"', 'permissions': ['admin']}, + 'authCertificateName': {'displayName': 'Auth Certificate', 'description': 'Auth Certificate name', 'type': 'string', 'order': '7', 'value': 'ca', 'default': 'ca', 'permissions': ['admin']}, + 'certificateName': {'displayName': 'Certificate Name', 'description': 'Certificate file name', 'type': 'string', 'order': '4', 'value': 'fledge', 'default': 'fledge', 'validity': 'enableHttp=="false"', 'permissions': ['admin']}, + 'authProviders': {'displayName': 'Auth Providers', 'description': 'Authentication providers to use for the interface (JSON array object)', 'type': 'JSON', 'order': '9', 'value': '{"providers": ["username", "ldap"] }', 'default': '{"providers": ["username", "ldap"] }', 'permissions': ['admin']}, 'authentication': {'displayName': 'Authentication', 'description': 'API Call Authentication', 'type': 'enumeration', 'options': ['mandatory', 'optional'], 'order': '5', 'value': 'optional', 'default': 'optional', 'permissions': ['admin']}, - 'authMethod': {'displayName': 'Authentication method', 'description': 'Authentication method', 'type': 'enumeration', 'options': ['any', 'password', 'certificate'], 'order': '6', 'value': 'any', 'default': 'any'}, - 'httpPort': {'displayName': 'HTTP Port', 'description': 'Port to accept HTTP connections on', 'type': 'integer', 'order': '2', 'value': '8081', 'default': '8081'}, - 'allowPing': {'displayName': 'Allow Ping', 'description': 'Allow access to ping, regardless of the authentication required and authentication header', 'type': 'boolean', 'order': '8', 'value': 'true', 'default': 'true'}, - 'enableHttp': {'displayName': 'Enable HTTP', 'description': 'Enable HTTP (disable to use HTTPS)', 'type': 'boolean', 'order': '1', 'value': 'true', 'default': 'true'}, + 'authMethod': {'displayName': 'Authentication method', 'description': 'Authentication method', 'type': 'enumeration', 'options': ['any', 'password', 'certificate'], 'order': '6', 'value': 'any', 'default': 'any', 'permissions': ['admin']}, + 'httpPort': {'displayName': 'HTTP Port', 'description': 'Port to accept HTTP connections on', 'type': 'integer', 'order': '2', 'value': '8081', 'default': '8081', 'permissions': ['admin']}, + 'allowPing': {'displayName': 'Allow Ping', 'description': 'Allow access to ping, regardless of the authentication required and authentication header', 'type': 'boolean', 'order': '8', 'value': 'true', 'default': 'true', 'permissions': ['admin']}, + 'enableHttp': {'displayName': 'Enable HTTP', 'description': 'Enable HTTP (disable to use HTTPS)', 'type': 'boolean', 'order': '1', 'value': 'true', 'default': 'true', 'permissions': ['admin']}, 'disconnectIdleUserSession': {'description': 'Disconnect idle user session after certain period of inactivity', 'type': 'integer', 'default': '15', 'displayName': 'Idle User Session Disconnection (In Minutes)', 'order': '10', 'minimum': '1', 'maximum': '1440', 'value': '15', 'permissions': ['admin']}} conn = http.client.HTTPConnection(fledge_url) conn.request("GET", '/fledge/category/rest_api') diff --git a/tests/system/python/packages/test_authentication.py b/tests/system/python/packages/test_authentication.py index 4669da7782..7fe55855d2 100644 --- a/tests/system/python/packages/test_authentication.py +++ b/tests/system/python/packages/test_authentication.py @@ -223,13 +223,12 @@ def _enable_tls(fledge_url, wait_time, auth): @pytest.fixture def generate_password_based_auth_token(asset_name, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("POST", "/fledge/login", json.dumps({"username": "user", "password": "fledge"})) + conn.request("POST", "/fledge/login", json.dumps({"username": "admin", "password": "fledge"})) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) assert LOGIN_SUCCESS_MSG == jdoc['message'] - assert not jdoc['admin'] global PASSWORD_TOKEN PASSWORD_TOKEN = jdoc["token"] From 761be108c9aed0dda157086818e711741b71cbec Mon Sep 17 00:00:00 2001 From: Aman <40791522+AmandeepArora@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:07:07 +0530 Subject: [PATCH 24/91] FOGL-8899: Fix for double free (#1418) * FOGL-8899: Fix for double free Signed-off-by: Amandeep Singh Arora * Further changes Signed-off-by: Amandeep Singh Arora --------- Signed-off-by: Amandeep Singh Arora --- C/plugins/storage/sqlite/common/connection.cpp | 4 ++-- C/plugins/storage/sqlite/common/readings.cpp | 7 +++++-- C/plugins/storage/sqlite/common/readings_catalogue.cpp | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/C/plugins/storage/sqlite/common/connection.cpp b/C/plugins/storage/sqlite/common/connection.cpp index 3fc01712bd..8c6f038659 100644 --- a/C/plugins/storage/sqlite/common/connection.cpp +++ b/C/plugins/storage/sqlite/common/connection.cpp @@ -3037,9 +3037,9 @@ char tmpbuf[512]; va_start(ap, reason); vsnprintf(tmpbuf, sizeof(tmpbuf), reason, ap); va_end(ap); - Logger::getLogger()->error("%s storage plugin raising error: %s", + Logger::getLogger()->error("%s storage plugin raising error: %s: %s", PLUGIN_LOG_NAME, - tmpbuf); + operation, tmpbuf); manager->setError(operation, tmpbuf, false); } diff --git a/C/plugins/storage/sqlite/common/readings.cpp b/C/plugins/storage/sqlite/common/readings.cpp index 224dc79ffe..f2fb7609e4 100644 --- a/C/plugins/storage/sqlite/common/readings.cpp +++ b/C/plugins/storage/sqlite/common/readings.cpp @@ -2246,9 +2246,9 @@ vector assetCodes; totTime += usecs; - if(usecs>150000) + if(usecs > 150000) { - std::this_thread::sleep_for(std::chrono::milliseconds(100+usecs/10000)); + std::this_thread::sleep_for(std::chrono::milliseconds(100 + usecs/1000)); } } @@ -2572,6 +2572,8 @@ struct timeval startTv, endTv; numReadings -= rowsAffected; rowcount -= rowsAffected; + sqlite3_free(zErrMsg); + // Release memory for 'query' var delete[] query; logger->debug(" Deleted :%lu: rows", rowsAffected); @@ -2712,6 +2714,7 @@ sqlite3_stmt *stmt; if (rc != SQLITE_OK) { raiseError("ReadingsAssetPurge", sqlite3_errmsg(dbHandle)); + sqlite3_free(zErrMsg); return 0; } diff --git a/C/plugins/storage/sqlite/common/readings_catalogue.cpp b/C/plugins/storage/sqlite/common/readings_catalogue.cpp index 640ab69e79..001c3ce473 100644 --- a/C/plugins/storage/sqlite/common/readings_catalogue.cpp +++ b/C/plugins/storage/sqlite/common/readings_catalogue.cpp @@ -2437,11 +2437,11 @@ int ReadingsCatalogue::purgeAllReadings(sqlite3 *dbHandle, const char *sqlCmdBa rc = SQLExec(dbHandle, sqlCmdTmp.c_str(), zErrMsg); - Logger::getLogger()->debug("purgeAllReadings: rc %d cmd '%s'", rc ,sqlCmdTmp.c_str() ); + Logger::getLogger()->debug("purgeAllReadings: rc:%d, errorMsg:'%s', cmd:'%s'", rc , (*zErrMsg) ? (*zErrMsg) : "", sqlCmdTmp.c_str() ); if (rc != SQLITE_OK) { - sqlite3_free(*zErrMsg); + // sqlite3_free(*zErrMsg); // needed by calling method break; } if (rowsAffected != nullptr) { From ccec65c800a058972861b99fee7a879cf1926011 Mon Sep 17 00:00:00 2001 From: Himanshu Vimal <67678828+cyberwalk3r@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:31:45 +0530 Subject: [PATCH 25/91] FOGL-8285: Reset lastFetchedId on source change. (#1419) * FOGL-8285: Reset lastFetchedId on source change. Signed-off-by: Himanshu Vimal --- C/services/north/data_load.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index afdb4cda8a..e44e6d1aed 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -105,12 +105,18 @@ void DataLoad::restart() */ bool DataLoad::setDataSource(const string& source) { - if (source.compare("statistics") == 0) + if (source.compare("statistics") == 0) { m_dataSource = SourceStatistics; - else if (source.compare("readings") == 0) + m_lastFetched = 0; // Reset on source change + } + else if (source.compare("readings") == 0) { m_dataSource = SourceReadings; - else if (source.compare("audit") == 0) + m_lastFetched = 0; // Reset on source change + } + else if (source.compare("audit") == 0) { m_dataSource = SourceAudit; + m_lastFetched = 0; // Reset on source change + } else { Logger::getLogger()->error("Unsupported source '%s' for north service '%s'", From 8330ab12db3e8cfe6c8fa82fdedc8cc1c500e47b Mon Sep 17 00:00:00 2001 From: Mohit04tomar <43023917+Mohit04tomar@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:18:45 +0530 Subject: [PATCH 26/91] FOGL-8787: Fix for system failing due to delay in update of statistics and change of state of service (#1393) --- tests/system/python/packages/test_omf_north_service.py | 10 ++++++---- tests/system/python/pair/test_c_north_service_pair.py | 8 +++++--- .../python/pair/test_pyton_north_service_pair.py | 8 +++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/system/python/packages/test_omf_north_service.py b/tests/system/python/packages/test_omf_north_service.py index f62e0ce042..3ccbfccf28 100644 --- a/tests/system/python/packages/test_omf_north_service.py +++ b/tests/system/python/packages/test_omf_north_service.py @@ -228,7 +228,7 @@ def test_omf_service_with_restart(self, clean_setup_fledge_packages, reset_fledg south_asset_name) def test_omf_service_with_enable_disable(self, reset_fledge, start_south_north, read_data_from_pi_web_api, - skip_verify_north_interface, + skip_verify_north_interface, wait_fix, fledge_url, wait_time, retries, pi_host, pi_port, pi_admin, pi_passwd, pi_db): """ Test OMF as a North service by enabling and disabling it. @@ -259,7 +259,8 @@ def test_omf_service_with_enable_disable(self, reset_fledge, start_south_north, put_url = "/fledge/schedule/{}".format(north_schedule_id) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) assert False == resp['schedule']['enabled'] - + print(f"Waiting for {wait_fix} seconds for delay caused by FOGL-8813 - tune pre-fetch buffers...") + time.sleep(wait_fix) data = {"enabled": "true"} put_url = "/fledge/schedule/{}".format(north_schedule_id) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) @@ -332,7 +333,7 @@ def test_omf_service_with_delete_add(self, reset_fledge, start_south_north, read south_asset_name) def test_omf_service_with_reconfig(self, reset_fledge, start_south_north, read_data_from_pi_web_api, - skip_verify_north_interface, fledge_url, + skip_verify_north_interface, fledge_url, wait_fix, wait_time, retries, pi_host, pi_port, pi_admin, pi_passwd, pi_db): """ Test OMF as a North service by reconfiguring it. reset_fledge: Fixture to reset fledge @@ -380,7 +381,8 @@ def test_omf_service_with_reconfig(self, reset_fledge, start_south_north, read_d put_url = "/fledge/category/{}".format(north_service_name) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) assert "Admin" == resp["PIWebAPIUserId"]["value"] - + print(f"Waiting for {wait_fix} seconds for delay caused by FOGL-8738 - north statistics update thread de-prioritised....") + time.sleep(wait_fix) old_ping_result = verify_ping(fledge_url, skip_verify_north_interface, wait_time, retries) # Wait for read and sent readings to increase diff --git a/tests/system/python/pair/test_c_north_service_pair.py b/tests/system/python/pair/test_c_north_service_pair.py index 8f1cf5ddb5..042807e919 100644 --- a/tests/system/python/pair/test_c_north_service_pair.py +++ b/tests/system/python/pair/test_c_north_service_pair.py @@ -341,7 +341,7 @@ def test_north_C_service_with_restart(self, clean_setup_fledge_packages, clean_i def test_north_C_service_with_enable_disable(self, setup_local, setup_remote, read_data_from_pi_web_api, remote_ip, skip_verify_north_interface, fledge_url, wait_time, retries, - pi_host, + pi_host, wait_fix, pi_admin, pi_passwd, pi_db): """ Test C plugin as a North service by disabling and enabling it. setup_local: Fixture to reset, add and configure plugins on local machine @@ -385,13 +385,15 @@ def test_north_C_service_with_enable_disable(self, setup_local, setup_remote, re put_url = "/fledge/schedule/{}".format(north_schedule_id) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) assert False == resp['schedule']['enabled'] - + print(f"Waiting for {wait_fix} seconds for delay caused by FOGL-8813 - tune pre-fetch buffers...") + time.sleep(wait_fix) # Enabling local machine north service data = {"enabled": "true"} put_url = "/fledge/schedule/{}".format(north_schedule_id) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) assert True == resp['schedule']['enabled'] - + print(f"Waiting for {wait_fix} seconds for delay caused by FOGL-8813 - tune pre-fetch buffers...") + time.sleep(wait_fix) old_ping_result = verify_ping(fledge_url, skip_verify_north_interface, wait_time, retries) old_ping_result_remote = verify_ping(fledge_url_remote, skip_verify_north_interface, wait_time, retries) # Wait for read and sent readings to increase diff --git a/tests/system/python/pair/test_pyton_north_service_pair.py b/tests/system/python/pair/test_pyton_north_service_pair.py index 818372fef6..3045f0df08 100644 --- a/tests/system/python/pair/test_pyton_north_service_pair.py +++ b/tests/system/python/pair/test_pyton_north_service_pair.py @@ -342,7 +342,7 @@ def test_north_python_service_with_restart(self, clean_setup_fledge_packages, cl def test_north_python_service_with_enable_disable(self, setup_local, setup_remote, read_data_from_pi_web_api, remote_ip, skip_verify_north_interface, fledge_url, wait_time, retries, - pi_host, + pi_host, wait_fix, pi_admin, pi_passwd, pi_db): """ Test python plugin as a North service by disabling and enabling it. setup_local: Fixture to reset, add and configure plugins on local machine @@ -386,13 +386,15 @@ def test_north_python_service_with_enable_disable(self, setup_local, setup_remot put_url = "/fledge/schedule/{}".format(north_schedule_id) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) assert False == resp['schedule']['enabled'] - + print(f"Waiting for {wait_fix} seconds for delay caused by FOGL-8813 - tune pre-fetch buffers...") + time.sleep(wait_fix) # Enabling local machine north service data = {"enabled": "true"} put_url = "/fledge/schedule/{}".format(north_schedule_id) resp = utils.put_request(fledge_url, urllib.parse.quote(put_url), data) assert True == resp['schedule']['enabled'] - + print(f"Waiting for {wait_fix} seconds for delay caused by FOGL-8813 - tune pre-fetch buffers...") + time.sleep(wait_fix) old_ping_result = verify_ping(fledge_url, skip_verify_north_interface, wait_time, retries) old_ping_result_remote = verify_ping(fledge_url_remote, skip_verify_north_interface, wait_time, retries) # Wait for read and sent readings to increase From 855d6ace72a63adeafbc9aff3570c048e79eb9d0 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 17 Jul 2024 11:42:31 +0530 Subject: [PATCH 27/91] permissions attribute handling in bucket config item Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index 31d47c5259..f705fba746 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -338,6 +338,21 @@ def get_entry_val(k): ''.format(category_name, item_name)) d = {entry_name: entry_val} expected_item_entries.update(d) + elif "permissions" in item_val: + permissions = item_val['permissions'] + if not isinstance(permissions, list): + raise ValueError( + 'For {} category, permissions entry value must be a list of string for item name {}; ' + 'got {}.'.format(category_name, item_name, type(permissions))) + if not permissions: + raise ValueError( + 'For {} category, permissions entry value must not be empty for item name {}.'.format( + category_name, item_name)) + else: + if not all(isinstance(ev, str) and ev != '' for ev in permissions): + raise ValueError('For {} category, permissions entry values must be a string and ' + 'non-empty for item name {}.'.format(category_name, item_name)) + item_val['permissions'] = permissions else: if type(entry_val) is not str: raise TypeError('For {} category, entry value must be a string for item name {} and ' From 74f8ab614651c316cf2cd9f52cccd4513ccf7544 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 17 Jul 2024 11:43:06 +0530 Subject: [PATCH 28/91] permissions unit tests for bucket config type added Signed-off-by: ashish-jabble --- .../common/test_configuration_manager.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/unit/python/fledge/common/test_configuration_manager.py b/tests/unit/python/fledge/common/test_configuration_manager.py index 154f612a43..94bf3fdf6d 100644 --- a/tests/unit/python/fledge/common/test_configuration_manager.py +++ b/tests/unit/python/fledge/common/test_configuration_manager.py @@ -546,7 +546,6 @@ async def test__validate_category_val_enum_type_bad(self, config, exception_name assert excinfo.type is exception_name assert exception_msg == str(excinfo.value) - #@pytest.mark.skip(reason="FOGL-8281") @pytest.mark.parametrize("config", [ ({ITEM_NAME: {"description": "test description", "type": "bucket", "default": "{'type': 'model', 'name': 'Person', 'version': '1.0', 'hardware': 'tpu'}", "properties": @@ -581,7 +580,10 @@ async def test__validate_category_val_enum_type_bad(self, config, exception_name "order": "2", "displayName": "Model version"}, "hardware": { "description": "Inference hardware (\'tpu\' may be chosen only if available and configured properly)", "type": "enumeration", "default": "cpu", "options": ["cpu", "tpu"], "order": "3", - "displayName": "Inference hardware"}}}}}) + "displayName": "Inference hardware"}}}}}), + ({ITEM_NAME: {"description": "Model Test", "type": "bucket", "properties": + {"key": {"name": {"description": "TFlite model name to use for inference", "type": "string", "default": + "People"}}}, "default": "A", "permissions": ["control"]}}) ]) async def test__validate_category_val_bucket_type_good(self, config): storage_client_mock = MagicMock(spec=StorageClientAsync) @@ -610,7 +612,20 @@ async def test__validate_category_val_bucket_type_good(self, config): CAT_NAME, ITEM_NAME)), ({ITEM_NAME: {"description": "test description", "type": "bucket", "default": {}, "properties": {"key": "v"}}}, TypeError, "For {} category, entry value must be a string for item name {} and entry name default; " - "got ".format(CAT_NAME, ITEM_NAME)) + "got ".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "bucket", "properties": {}, "default": "A", "permissions": ""}}, + ValueError, "For {} category, permissions entry value must be a list of string for item name {}; " + "got .".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "bucket", "properties": {}, "default": "A", "permissions": [""]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "bucket", "properties": {}, "default": "A", "permissions": [2]}}, + ValueError,"For {} category, permissions entry values must be a string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)), + ({ITEM_NAME: {"description": "test", "type": "bucket", "properties": {}, "default": "A", + "permissions": ["user", 5]}}, + ValueError, "For {} category, permissions entry values must be a string and non-empty for item name {}." + "".format(CAT_NAME, ITEM_NAME)) ]) async def test__validate_category_val_bucket_type_bad(self, config, exc_name, reason): storage_client_mock = MagicMock(spec=StorageClientAsync) From 8f440b387f317a0afc1797649b82dbde400fbd46 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 17 Jul 2024 15:28:49 +0530 Subject: [PATCH 29/91] critical option removed from core logging; to fix consistency as with C log Signed-off-by: ashish-jabble --- python/fledge/services/core/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index 2eab1580e2..e61a01b530 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -299,7 +299,7 @@ class Server: 'description': 'Minimum logging level reported for Core server', 'type': 'enumeration', 'displayName': 'Minimum Log Level', - 'options': ['debug', 'info', 'warning', 'error', 'critical'], + 'options': ['debug', 'info', 'warning', 'error'], 'default': 'warning', 'order': '1' } From 3f9e802c7799e92d300e2629caa108e6f797884f Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 19 Jul 2024 11:14:52 +0530 Subject: [PATCH 30/91] setuptools pip dep fixes when python version is greater than 3.8 Signed-off-by: ashish-jabble --- python/requirements-test.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/requirements-test.txt b/python/requirements-test.txt index 4e6ba3ddf8..ed1a6df98b 100644 --- a/python/requirements-test.txt +++ b/python/requirements-test.txt @@ -15,3 +15,7 @@ pyserial==3.4 # keep this in sync with requirement.txt, as otherwise pytest-aiohttp always pulls the latest aiohttp==3.8.1 yarl==1.7.2 + +# specific version for setuptools as per pytest version +setuptools==70.3.0;python_version>="3.8" + From d30e14b11783b4913e3e7daa89cff06be46e1db6 Mon Sep 17 00:00:00 2001 From: pintomax Date: Mon, 22 Jul 2024 11:31:49 +0200 Subject: [PATCH 31/91] FOGL-8912: Merge develop3.0 (#1428) Signed-off-by: ashish-jabble Signed-off-by: Mark Riddoch Co-authored-by: ashish-jabble Co-authored-by: Mark Riddoch --- C/common/filter_pipeline.cpp | 299 +++++++----------- C/common/filter_plugin.cpp | 2 +- C/common/include/filter_pipeline.h | 20 +- C/common/include/pipeline_element.h | 138 +++++++- C/common/pipeline_branch.cpp | 269 ++++++++++++++++ C/common/pipeline_element.cpp | 18 ++ C/common/pipeline_filter.cpp | 215 +++++++++++++ C/common/pipeline_writer.cpp | 55 ++++ C/common/reading.cpp | 8 +- C/common/reading_set.cpp | 12 +- C/services/north/data_load.cpp | 14 +- C/services/south/include/ingest.h | 1 + C/services/south/ingest.cpp | 39 +-- .../sending_process/north_filter_pipeline.cpp | 45 +-- C/tasks/north/sending_process/sending.cpp | 2 +- .../north/sending_process/sending_process.cpp | 6 +- .../rapidjson/example/simpledom/simpledom.cpp | 11 +- VERSION | 2 +- python/fledge/common/service_record.py | 1 + python/fledge/services/core/api/service.py | 17 +- .../services/core/scheduler/scheduler.py | 8 +- scripts/fledge | 1 + .../plugins/storage/postgres/downgrade/73.sql | 1 + scripts/plugins/storage/postgres/init.sql | 1 + .../plugins/storage/postgres/upgrade/74.sql | 2 + .../plugins/storage/sqlite/downgrade/73.sql | 1 + scripts/plugins/storage/sqlite/init.sql | 1 + scripts/plugins/storage/sqlite/upgrade/74.sql | 2 + .../plugins/storage/sqlitelb/downgrade/73.sql | 1 + scripts/plugins/storage/sqlitelb/init.sql | 2 + .../plugins/storage/sqlitelb/upgrade/74.sql | 2 + scripts/services/pipeline_c | 29 ++ .../fledge/common/test_service_record.py | 17 +- .../fledge/services/core/api/test_service.py | 2 +- 34 files changed, 952 insertions(+), 292 deletions(-) create mode 100644 C/common/pipeline_branch.cpp create mode 100644 C/common/pipeline_element.cpp create mode 100644 C/common/pipeline_filter.cpp create mode 100644 C/common/pipeline_writer.cpp create mode 100644 scripts/plugins/storage/postgres/downgrade/73.sql create mode 100644 scripts/plugins/storage/postgres/upgrade/74.sql create mode 100644 scripts/plugins/storage/sqlite/downgrade/73.sql create mode 100644 scripts/plugins/storage/sqlite/upgrade/74.sql create mode 100644 scripts/plugins/storage/sqlitelb/downgrade/73.sql create mode 100644 scripts/plugins/storage/sqlitelb/upgrade/74.sql create mode 100755 scripts/services/pipeline_c diff --git a/C/common/filter_pipeline.cpp b/C/common/filter_pipeline.cpp index ad046bd64b..ec09762737 100755 --- a/C/common/filter_pipeline.cpp +++ b/C/common/filter_pipeline.cpp @@ -131,86 +131,13 @@ bool FilterPipeline::loadFilters(const string& categoryName) Logger::getLogger()->info(logMsg.c_str()); - // Try loading all filter plugins: abort on any error - for (Value::ConstValueIterator itr = filterList.Begin(); itr != filterList.End(); ++itr) - { - if (itr->IsString()) - { - // Get "plugin" item fromn filterCategoryName - string filterCategoryName = itr->GetString(); - ConfigCategory filterDetails = mgtClient->getCategory(filterCategoryName); - if (!filterDetails.itemExists("plugin")) - { - string errMsg("loadFilters: 'plugin' item not found "); - errMsg += "in " + filterCategoryName + " category"; - Logger::getLogger()->fatal(errMsg.c_str()); - throw runtime_error(errMsg); - } - string filterName = filterDetails.getValue("plugin"); - PLUGIN_HANDLE filterHandle; - // Load filter plugin only: we don't call any plugin method right now - filterHandle = loadFilterPlugin(filterName); - if (!filterHandle) - { - string errMsg("Cannot load filter plugin '" + filterName + "'"); - Logger::getLogger()->fatal(errMsg.c_str()); - throw runtime_error(errMsg); - } - else - { - // Save filter handler: key is filterCategoryName - filterInfo.push_back(pair - (filterCategoryName, filterHandle)); - } - } - else if (itr->IsArray()) - { - // Sub pipeline - Logger::getLogger()->warn("This version of Fledge does not support branching of pipelines. The branch will be ignored."); - } - else if (itr->IsObject()) - { - // An object, probably the write destination - Logger::getLogger()->warn("This version of Fledge does not support pipelines with different destinations. The destination will be ignored and the data written to the default storage service."); - } - else - { - Logger::getLogger()->error("Unexpected object in pipeline definition %s, ignoring", categoryName.c_str()); - } - } + loadPipeline(filterList, m_filters); // We have kept filter default config in the filterInfo map // Handle configuration for each filter - PluginManager *pluginManager = PluginManager::getInstance(); - for (vector>::iterator itr = filterInfo.begin(); - itr != filterInfo.end(); - ++itr) + for (auto& itr : m_filters) { - // Get plugin default configuration - string filterConfig = pluginManager->getInfo(itr->second)->config; - - // Create/Update default filter category items - DefaultConfigCategory filterDefConfig(categoryName + "_" + itr->first, filterConfig); - string filterDescription = "Configuration of '" + itr->first; - filterDescription += "' filter for plugin '" + categoryName + "'"; - filterDefConfig.setDescription(filterDescription); - - if (!mgtClient->addCategory(filterDefConfig, true)) - { - string errMsg("Cannot create/update '" + \ - categoryName + "' filter category"); - Logger::getLogger()->fatal(errMsg.c_str()); - throw runtime_error(errMsg); - } - children.push_back(categoryName + "_" + itr->first); - - // Instantiate the FilterPlugin class - // in order to call plugin entry points - FilterPlugin* currentFilter = new FilterPlugin(itr->first, - itr->second); - - // Add filter to filters vector - m_filters.push_back(currentFilter); + itr->setupConfiguration(mgtClient, children); } } } @@ -248,6 +175,43 @@ bool FilterPipeline::loadFilters(const string& categoryName) } } +void FilterPipeline::loadPipeline(const Value& filterList, vector& pipeline) +{ + // Try loading all filter plugins: abort on any error + for (Value::ConstValueIterator itr = filterList.Begin(); itr != filterList.End(); ++itr) + { + if (itr->IsString()) + { + // Get "plugin" item from filterCategoryName + string filterCategoryName = itr->GetString(); + Logger::getLogger()->info("Creating pipeline filter %s", filterCategoryName.c_str()); + ConfigCategory filterDetails = mgtClient->getCategory(filterCategoryName); + + PipelineFilter *element = new PipelineFilter(filterCategoryName, filterDetails); + element->setServiceName(serviceName); + element->setStorage(&storage); + pipeline.emplace_back(element); + } + else if (itr->IsArray()) + { + // Sub pipeline + Logger::getLogger()->info("Creating pipeline branch"); + PipelineBranch *element = new PipelineBranch(this); + loadPipeline(*itr, element->getBranchElements()); + pipeline.emplace_back(element); + } + else if (itr->IsObject()) + { + // An object, probably the write destination + Logger::getLogger()->warn("This version of Fledge does not support pipelines with different destinations. The destination will be ignored and the data written to the default storage service."); + } + else + { + Logger::getLogger()->error("Unexpected object in pipeline definition, ignoring"); + } + } +} + /** * Set the filter pipeline * @@ -268,76 +232,46 @@ bool FilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *useFil string errMsg = "'plugin_init' failed for filter '"; for (auto it = m_filters.begin(); it != m_filters.end(); ++it) { - string filterCategoryName = serviceName + "_" + (*it)->getName(); - ConfigCategory updatedCfg; - vector children; try { - Logger::getLogger()->info("Load plugin categoryName %s", filterCategoryName.c_str()); - // Fetch up to date filter configuration - updatedCfg = mgtClient->getCategory(filterCategoryName); - - // Pass Management client IP:Port to filter so that it may connect to bucket service - updatedCfg.addItem("mgmt_client_url_base", "Management client host and port", - "string", "127.0.0.1:0", - mgtClient->getUrlbase()); - - // Add filter category name under service/process config name - children.push_back(filterCategoryName); - mgtClient->addChildCategories(serviceName, children); - - ConfigHandler *configHandler = ConfigHandler::getInstance(mgtClient); - configHandler->registerCategory((ServiceHandler *)ingest, filterCategoryName); - m_serviceHandler = (ServiceHandler *)ingest; - - m_filterCategories[filterCategoryName] = (*it); - } - // TODO catch specific exceptions - catch (...) - { - throw; - } - - // Iterate the load filters set in the Ingest class m_filters member - if ((it + 1) != m_filters.end()) - { - // Set next filter pointer as OUTPUT_HANDLE - if (!(*it)->init(updatedCfg, - (OUTPUT_HANDLE *)(*(it + 1)), - filterReadingSetFn(passToOnwardFilter))) + if ((*it)->isBranch()) { - errMsg += (*it)->getName() + "'"; - initErrors = true; - break; + Logger::getLogger()->info("Set branch functions"); + PipelineBranch *branch = (PipelineBranch *)(*it); + branch->setFunctions(passToOnwardFilter, useFilteredData, ingest); } - } - else - { - // Set the Ingest class pointer as OUTPUT_HANDLE - if (!(*it)->init(updatedCfg, - (OUTPUT_HANDLE *)(ingest), - filterReadingSetFn(useFilteredData))) + Logger::getLogger()->info("Setup element %s", (*it)->getName().c_str()); + (*it)->setup(mgtClient, ingest, m_filterCategories); + // Iterate the load filters set in the Ingest class m_filters member + if ((it + 1) != m_filters.end()) { - errMsg += (*it)->getName() + "'"; - initErrors = true; - break; + (*it)->setNext(*(it + 1)); + // Set next filter pointer as OUTPUT_HANDLE + if (!(*it)->init((OUTPUT_HANDLE *)(*(it + 1)), + filterReadingSetFn(passToOnwardFilter))) + { + errMsg += (*it)->getName() + "'"; + initErrors = true; + break; + } + } + else + { + // Set the Ingest class pointer as OUTPUT_HANDLE + if (!(*it)->init((OUTPUT_HANDLE *)(ingest), + filterReadingSetFn(useFilteredData))) + { + errMsg += (*it)->getName() + "'"; + initErrors = true; + break; + } } } - - if ((*it)->persistData()) - { - // Plugin support SP_PERSIST_DATA - // Instantiate the PluginData class - (*it)->m_plugin_data = new PluginData(&storage); - // Load plugin data from storage layer - string pluginStoredData = (*it)->m_plugin_data->loadStoredData(serviceName + (*it)->getName()); - //call 'plugin_start' with plugin data: startData() - (*it)->startData(pluginStoredData); - } - else - { - // We don't call simple plugin_start for filters right now + // TODO catch specific exceptions + catch (...) + { + throw; } } @@ -368,35 +302,12 @@ void FilterPipeline::cleanupFilters(const string& categoryName) // Cleanup filters, in reverse order for (auto it = m_filters.rbegin(); it != m_filters.rend(); ++it) { - FilterPlugin* filter = *it; - string filterCategoryName = categoryName + "_" + filter->getName(); + PipelineElement *element = *it; ConfigHandler *configHandler = ConfigHandler::getInstance(mgtClient); - configHandler->unregisterCategory(m_serviceHandler, filterCategoryName); - Logger::getLogger()->info("FilterPipeline::cleanupFilters(): unregistered category %s", filterCategoryName.c_str()); - - // If plugin has SP_PERSIST_DATA option: - if (filter->m_plugin_data) - { - // 1- call shutdownSaveData and get up-to-date plugin data. - string saveData = filter->shutdownSaveData(); - // 2- store returned data: key is service/task categoryName + pluginName - string key(categoryName + filter->getName()); - if (!filter->m_plugin_data->persistPluginData(key, saveData)) - { - Logger::getLogger()->error("Filter plugin %s has failed to save data [%s] for key %s", - filter->getName().c_str(), - saveData.c_str(), - key.c_str()); - } - } - else - { - // Call filter plugin shutdown - filter->shutdown(); - } + element->shutdown(m_serviceHandler, configHandler); // Free filter - delete filter; + delete element; } } @@ -410,23 +321,6 @@ void FilterPipeline::cleanupFilters(const string& categoryName) */ void FilterPipeline::configChange(const string& category, const string& newConfig) { - Logger::getLogger()->debug("%s:%d: category=%s, newConfig=%s", __FUNCTION__, __LINE__, category.c_str(), newConfig.c_str()); - - if(newConfig.compare("logLevel") == 0) - { - PluginManager *pluginManager = PluginManager::getInstance(); - Logger::getLogger()->debug("m_filterCategories has %d entries", m_filterCategories.size()); - for(auto & it : m_filterCategories) - { - const string &filtername = it.first; - FilterPlugin *fp = it.second; - PLUGIN_TYPE type = pluginManager->getPluginImplType(fp->getHandle()); - Logger::getLogger()->debug("%s:%d: filter name=%s, filter type = %s", __FUNCTION__, __LINE__, filtername.c_str(), (type==PYTHON_PLUGIN)?"PYTHON_PLUGIN":"BINARY_PLUGIN"); - if(type == PYTHON_PLUGIN) - fp->reconfigure(newConfig); - } - } - auto it = m_filterCategories.find(category); if (it != m_filterCategories.end()) { @@ -434,3 +328,46 @@ void FilterPipeline::configChange(const string& category, const string& newConfi } } +/** + * Called when we pass the data into the pipeline. Set the + * number of active branches to 1 + */ +void FilterPipeline::execute() +{ + unique_lock lck(m_actives); + m_activeBranches = 1; +} + +/** + * Wait for all active branches of the pipeline to complete + */ +void FilterPipeline::awaitCompletion() +{ + unique_lock lck(m_actives); + while (m_activeBranches > 0) + { + m_branchActivations.wait(lck); + } +} + +/** + * A new branch has started in the pipeline + */ +void FilterPipeline::startBranch() +{ + unique_lock lck(m_actives); + m_activeBranches++; +} + +/** + * A branch in the pipeline has completed + */ +void FilterPipeline::completeBranch() +{ + unique_lock lck(m_actives); + m_activeBranches--; + if (m_activeBranches == 0) + { + m_branchActivations.notify_all(); + } +} diff --git a/C/common/filter_plugin.cpp b/C/common/filter_plugin.cpp index 56f4074b50..91878e0177 100644 --- a/C/common/filter_plugin.cpp +++ b/C/common/filter_plugin.cpp @@ -73,7 +73,7 @@ FilterPlugin::~FilterPlugin() * Call the loaded plugin "plugin_init" method * * @param config The filter configuration - * @param outHandle The ouutput_handled passed with + * @param outHandle The output_handled passed with * filtered data to OUTPUT_STREAM function * @param outputFunc The output_stream function pointer * the filter uses to pass data out diff --git a/C/common/include/filter_pipeline.h b/C/common/include/filter_pipeline.h index 6c3f7c4029..8dc1b091fb 100644 --- a/C/common/include/filter_pipeline.h +++ b/C/common/include/filter_pipeline.h @@ -17,7 +17,7 @@ #include #include #include - +#include typedef void (*filterReadingSetFn)(OUTPUT_HANDLE *outHandle, READINGSET* readings); /** @@ -33,7 +33,7 @@ class FilterPipeline StorageClient& storage, std::string serviceName); ~FilterPipeline(); - FilterPlugin * getFirstFilterPlugin() + PipelineElement *getFirstFilterPlugin() { return (m_filters.begin() == m_filters.end()) ? NULL : *(m_filters.begin()); @@ -54,22 +54,30 @@ class FilterPipeline bool hasChanged(const std::string pipeline) const { return m_pipeline != pipeline; } bool isShuttingDown() { return m_shutdown; }; void setShuttingDown() { m_shutdown = true; } + void execute(); + void awaitCompletion(); + void startBranch(); + void completeBranch(); private: PLUGIN_HANDLE loadFilterPlugin(const std::string& filterName); + void loadPipeline(const rapidjson::Value& filters, std::vector& pipeline); protected: ManagementClient* mgtClient; StorageClient& storage; std::string serviceName; - std::vector + std::vector m_filters; - std::map + std::map m_filterCategories; std::string m_pipeline; - bool m_ready; - bool m_shutdown; + bool m_ready; + bool m_shutdown; ServiceHandler *m_serviceHandler; + int m_activeBranches; + std::mutex m_actives; + std::condition_variable m_branchActivations; }; #endif diff --git a/C/common/include/pipeline_element.h b/C/common/include/pipeline_element.h index 1badf33403..6f1c880218 100644 --- a/C/common/include/pipeline_element.h +++ b/C/common/include/pipeline_element.h @@ -9,19 +9,72 @@ * * Author: Mark Riddoch */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class FilterPipeline; /** * The base pipeline element class */ class PipelineElement { public: - PipelineElement(); + PipelineElement() : m_next(NULL), m_storage(NULL) {}; + virtual ~PipelineElement() {}; void setNext(PipelineElement *next) { m_next = next; - } - private: + }; + PipelineElement *getNext() + { + return m_next; + }; + void setService(const std::string& serviceName) + { + m_serviceName = serviceName; + }; + void setStorage(StorageClient *storage) + { + m_storage = storage; + }; + static void ingest(void *handle, READINGSET *readings) + { + ((PipelineElement *)handle)->ingest(readings); + }; + virtual bool setupConfiguration(ManagementClient *mgtClient, + std::vector& children) + { + return false; + }; + virtual bool isFilter() + { + return false; + }; + virtual bool isBranch() + { + return false; + }; + virtual void ingest(READINGSET *readingSet) = 0; + virtual bool setup(ManagementClient *mgmt, void *ingest, std::map& categories) = 0; + virtual bool init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output) = 0; + virtual void shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler) = 0; + virtual void reconfigure(const std::string& newConfig) + { + }; + virtual std::string getName() = 0; + virtual bool isReady() = 0; + protected: + std::string m_serviceName; PipelineElement *m_next; + StorageClient *m_storage; }; @@ -30,9 +83,42 @@ class PipelineElement { */ class PipelineFilter : public PipelineElement { public: - PipelineFilter(const std::string& name); + PipelineFilter(const std::string& name, const ConfigCategory& filterDetails); + ~PipelineFilter(); + bool setupConfiguration(ManagementClient *mgtClient, std::vector& children); + void ingest(READINGSET *readingSet) + { + if (m_plugin) + { + m_plugin->ingest(readingSet); + } + else + { + Logger::getLogger()->error("Pipeline filter %s has no plugin associated with it.", m_name.c_str()); + } + }; + bool setup(ManagementClient *mgmt, void *ingest, std::map& categories); + bool init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output); + void shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler); + void reconfigure(const std::string& newConfig); + bool isFilter() { return true; }; + std::string getCategoryName() { return m_categoryName; }; + bool persistData() { return m_plugin->persistData(); }; + void setPluginData(PluginData *data) { m_plugin->m_plugin_data = data; }; + std::string getPluginData() { return m_plugin->m_plugin_data->loadStoredData(m_serviceName + m_name); }; + void setServiceName(const std::string& name) { m_serviceName = name; }; + std::string getName() { return m_name; }; + bool isReady() { return true; }; + private: + PLUGIN_HANDLE loadFilterPlugin(const std::string& filterName); private: + std::string m_name; // The name of the filter instance + std::string m_categoryName; + std::string m_pluginName; + PLUGIN_HANDLE m_handle; FilterPlugin *m_plugin; + std::string m_serviceName; + ConfigCategory m_updatedCfg; }; /** @@ -40,7 +126,44 @@ class PipelineFilter : public PipelineElement { */ class PipelineBranch : public PipelineElement { public: - PipelineBranch(); + PipelineBranch(FilterPipeline *parent); + ~PipelineBranch(); + void ingest(READINGSET *readingSet); + std::string getName() { return "Branch"; }; + bool setupConfiguration(ManagementClient *mgtClient, std::vector& children); + bool setup(ManagementClient *mgmt, void *ingest, std::map& categories); + bool init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output); + void shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler); + bool isReady(); + bool isBranch() + { + return true; + }; + std::vector& + getBranchElements() + { + return m_branch; + }; + void setFunctions(void *onward, void *use, void *ingest) + { + m_passOnward = onward; + m_useData = use; + m_ingest = ingest; + }; + private: + static void branchHandler(void *instance); + void handler(); + private: + std::vector m_branch; + std::thread *m_thread; + std::queue m_queue; + std::mutex m_mutex; + std::condition_variable m_cv; + void *m_passOnward; + void *m_useData; + void *m_ingest; + bool m_shutdownCalled; + FilterPipeline *m_pipeline; }; /** @@ -49,6 +172,11 @@ class PipelineBranch : public PipelineElement { class PipelineWriter : public PipelineElement { public: PipelineWriter(); + void ingest(READINGSET *readingSet); + bool setup(ManagementClient *mgmt, void *ingest, std::map& categories); + bool init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output); + void shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler); + bool isReady(); }; #endif diff --git a/C/common/pipeline_branch.cpp b/C/common/pipeline_branch.cpp new file mode 100644 index 0000000000..8c70e24984 --- /dev/null +++ b/C/common/pipeline_branch.cpp @@ -0,0 +1,269 @@ +/* + * Fledge pipeline branch class + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Mark Riddoch + */ +#include +#include +#include +#include +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +using namespace std; + +/** + * Constructor for a branch in a filter pipeline + */ +PipelineBranch::PipelineBranch(FilterPipeline *parent) : m_pipeline(parent), m_thread(NULL), PipelineElement() +{ + m_shutdownCalled = false; +} + +/** + * Destructor for the pipeline branch + * + * If the pipeline is not already shutdown then shut it down + * Delete the thread if it exists. + */ +PipelineBranch::~PipelineBranch() +{ + if (!m_shutdownCalled) + { + m_shutdownCalled = true; + m_cv.notify_all(); + if (m_thread->joinable()) + m_thread->join(); + } + if (m_thread) + { + delete m_thread; + } + + // Clear any queued readings + while (!m_queue.empty()) + { + ReadingSet *readings = m_queue.front(); + m_queue.pop(); + delete readings; + } + for (auto it = m_branch.begin(); it != m_branch.end(); ++it) + { + delete *it; + } +} + +/** + * Setup the configuration for a branch in a pipeline + * + * @param mgtClient The management client + * @param children A vector to fill with child configuration categories + */ +bool PipelineBranch::setupConfiguration(ManagementClient *mgtClient, vector& children) +{ + for (auto it = m_branch.begin(); it != m_branch.end(); ++it) + { + (*it)->setupConfiguration(mgtClient, children); + } + return true; +} + +/** + * Setup the configuration categories for the branch element of + * a pipeline. The branch itself has no category, but it must call + * the setup method on all items in the child branch of the + * piepline. + * + * @param mgmt The management client + * @param ingest The configuration handler for our service + * @param filterCategories A map of the category names to pipeline elements + */ +bool PipelineBranch::setup(ManagementClient *mgmt, void *ingest, map& filterCategories) +{ +vector children; + + for (auto it = m_branch.begin(); it != m_branch.end(); ++it) + { + if ((*it)->isBranch()) + { + PipelineBranch *branch = (PipelineBranch *)(*it); + branch->setFunctions(m_passOnward, m_useData, m_ingest); + } + (*it)->setup(mgmt, ingest, filterCategories); + } + return true; +} +/** + * Initialise the pipeline branch. + * + * Initialise the elements of the child pipeline + * Spawn a thread to excute the child pipeline. + * + * @param config The filter configuration + * @param outHandle The pipeline element on the "main branch" + * @param output + */ +bool PipelineBranch::init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output) +{ + bool initErrors = false; + string errMsg = "'plugin_init' failed for filter '"; + for (auto it = m_branch.begin(); it != m_branch.end(); ++it) + { + try + { + Logger::getLogger()->info("Initialise %s on pipeline branch", (*it)->getName().c_str()); + // Iterate the load filters set in the Ingest class m_filters member + if ((it + 1) != m_branch.end()) + { + (*it)->setNext(*(it + 1)); + // Set next filter pointer as OUTPUT_HANDLE + if (!(*it)->init((OUTPUT_HANDLE *)(*(it + 1)), + filterReadingSetFn(m_passOnward))) + { + errMsg += (*it)->getName() + "'"; + initErrors = true; + break; + } + } + else + { + // Set the Ingest class pointer as OUTPUT_HANDLE + if (!(*it)->init((OUTPUT_HANDLE *)(m_ingest), + filterReadingSetFn(m_useData))) + { + errMsg += (*it)->getName() + "'"; + initErrors = true; + break; + } + } + + } + // TODO catch specific exceptions + catch (...) + { + throw; + } + } + + if (initErrors) + { + // Failure + Logger::getLogger()->fatal("%s error: %s", __FUNCTION__, errMsg.c_str()); + return false; + } + + Logger::getLogger()->debug("Create branch handler thread"); + m_thread = new thread(PipelineBranch::branchHandler, this); + + //Success + return true; +} + +/** + * Ingest a set of readings and pass on in the pipeline. Create a deep copy + * and queue the copy into the branched pipeline. + * + * @param readingSet The set of readings to ingest + */ +void PipelineBranch::ingest(READINGSET *readingSet) +{ + m_pipeline->startBranch(); + READINGSET *copy = new ReadingSet(); + copy->copy(*readingSet); + unique_lock lck(m_mutex); + m_queue.push(copy); + lck.unlock(); + m_cv.notify_one(); + if (m_next) + { + m_next->ingest(readingSet); + } + else + { + Logger::getLogger()->warn("Pipeline branch has no downstream element"); + } +} + +/** + * Setup the configuration categories for the branch element of + * a pipeline. The branch itself has no category, but it must call + * the setup method on all items in the child branch of the + * piepline. + * + * @param mgmt The management client + * @param ingest The configuration handler for our service + * @param filterCategories A map of the category names to pipeline elements + */ +void PipelineBranch::shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler) +{ + // Shutdown the handler thread + m_shutdownCalled = true; + m_cv.notify_all(); + m_thread->join(); + delete m_thread; + m_thread = NULL; + + // Shutdown the filter elements on the branch + for (auto it = m_branch.begin(); it != m_branch.end(); ++it) + { + (*it)->shutdown(serviceHandler, configHandler); + } + + // Clear any queued readings + while (!m_queue.empty()) + { + ReadingSet *readings = m_queue.front(); + m_queue.pop(); + delete readings; + } +} + +/** + * Return if the branch is ready to be executed + */ +bool PipelineBranch::isReady() +{ + return true; +} + +/** + * Static entry point for the thread that handles sending data on the + * branch + * + * @param instance The instance of the PipelineBranch + */ +void PipelineBranch::branchHandler(void *instance) +{ + PipelineBranch *branch = (PipelineBranch *)instance; + branch->handler(); +} + +/** + * The handler for readings in an instance of a branch. + * Loop waiting for data or a shutdown signal and pass the + * queued data to the first filter in the pipeline branch + */ +void PipelineBranch::handler() +{ + Logger::getLogger()->info("Starting thread to process branch pipeline"); + while (!m_shutdownCalled) + { + unique_lock lck(m_mutex); + while (m_queue.empty()) + { + m_cv.wait(lck); + if (m_shutdownCalled) + { + return; + } + } + ReadingSet *readings = m_queue.front(); + m_queue.pop(); + lck.unlock(); + m_branch[0]->ingest(readings); + } +} diff --git a/C/common/pipeline_element.cpp b/C/common/pipeline_element.cpp new file mode 100644 index 0000000000..40e7136b89 --- /dev/null +++ b/C/common/pipeline_element.cpp @@ -0,0 +1,18 @@ +/* + * Fledge pipeline element classes + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Mark Riddoch + */ +#include +#include +#include +#include +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +using namespace std; + diff --git a/C/common/pipeline_filter.cpp b/C/common/pipeline_filter.cpp new file mode 100644 index 0000000000..648d9faee5 --- /dev/null +++ b/C/common/pipeline_filter.cpp @@ -0,0 +1,215 @@ +/* + * Fledge pipeline filter class + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Mark Riddoch + */ +#include +#include +#include +#include +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +using namespace std; + + +/** + * Construct the PipelineFilter class. This is the + * specialisation of the PipelineElement that represents + * a running filter in the pipeline. + */ +PipelineFilter::PipelineFilter(const string& name, const ConfigCategory& filterDetails) : + PipelineElement(), m_name(name), m_plugin(NULL) +{ + m_name = name; + if (!filterDetails.itemExists("plugin")) + { + string errMsg("loadFilters: 'plugin' item not found "); + errMsg += "in " + m_name + " category"; + Logger::getLogger()->fatal(errMsg.c_str()); + throw runtime_error(errMsg); + } + m_pluginName = filterDetails.getValue("plugin"); + // Load filter plugin only: we don't call any plugin method right now + m_handle = loadFilterPlugin(m_pluginName); + if (!m_handle) + { + string errMsg("Cannot load filter plugin '" + m_pluginName + "'"); + Logger::getLogger()->fatal(errMsg.c_str()); + throw runtime_error(errMsg); + } +} + +/** + * Destructor for the pipeline filter element + */ +PipelineFilter::~PipelineFilter() +{ + delete m_plugin; +} + +/** + * Setup the configuration for a filter in a pipeline + * + * @param mgtClient The managament client + * @param children A vector to fill with child configuration categories + */ +bool PipelineFilter::setupConfiguration(ManagementClient *mgtClient, vector& children) +{ + PluginManager *manager = PluginManager::getInstance(); + string filterConfig = manager->getInfo(m_handle)->config; + + m_categoryName = m_serviceName + "_" + m_name; + // Create/Update default filter category items + DefaultConfigCategory filterDefConfig(m_categoryName, filterConfig); + string filterDescription = "Configuration of '" + m_name; + filterDescription += "' filter for plugin '" + m_pluginName + "'"; + filterDefConfig.setDescription(filterDescription); + + if (!mgtClient->addCategory(filterDefConfig, true)) + { + string errMsg("Cannot create/update '" + \ + m_categoryName + "' filter category"); + Logger::getLogger()->fatal(errMsg.c_str()); + return false; + } + children.push_back(m_serviceName + '_' + m_name); + + // Instantiate the FilterPlugin class + // in order to call plugin entry points + m_plugin = new FilterPlugin(m_name, m_handle); + if (!m_plugin) + return false; + return true; +} + + +/** + * Load the specified filter plugin + * + * @param filterName The filter plugin to load + * @return Plugin handle on success, NULL otherwise + * + */ +PLUGIN_HANDLE PipelineFilter::loadFilterPlugin(const string& filterName) +{ + if (filterName.empty()) + { + Logger::getLogger()->error("Unable to fetch filter plugin '%s' from configuration.", + filterName.c_str()); + // Failure + return NULL; + } + Logger::getLogger()->info("Loading filter plugin '%s'.", filterName.c_str()); + + PluginManager *manager = PluginManager::getInstance(); + PLUGIN_HANDLE handle; + if ((handle = manager->loadPlugin(filterName, PLUGIN_TYPE_FILTER)) != NULL) + { + // Success + Logger::getLogger()->info("Loaded filter plugin '%s'.", filterName.c_str()); + } + return handle; +} + +/** + * Setup the configuration categories for the filter + * element in a pipeline + * + * @param mgmt The Management client + * @param ingest The service handler for our service + * @param filterCatiegories A map of the category name to pipeline element + */ +bool PipelineFilter::setup(ManagementClient *mgmt, void *ingest, map& filterCategories) +{ +vector children; + + Logger::getLogger()->info("Load plugin categoryName %s for %s", m_categoryName.c_str(), m_name.c_str()); + // Fetch up to date filter configuration + m_updatedCfg = mgmt->getCategory(m_categoryName); + + // Pass Management client IP:Port to filter so that it may connect to bucket service + m_updatedCfg.addItem("mgmt_client_url_base", "Management client host and port", + "string", "127.0.0.1:0", + mgmt->getUrlbase()); + + // Add filter category name under service/process config name + children.push_back(m_categoryName); + mgmt->addChildCategories(m_serviceName, children); + + ConfigHandler *configHandler = ConfigHandler::getInstance(mgmt); + configHandler->registerCategory((ServiceHandler *)ingest, m_categoryName); + filterCategories[m_categoryName] = this; + + return true; +} + +/** + * Initialise the pipeline filter ready for ingest of data + * + * @param outHandle The pipeline element we are sending the data to + * @param output + */ +bool PipelineFilter::init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output) +{ + m_plugin->init(m_updatedCfg, outHandle, output); + if (m_plugin->persistData()) + { + // Plugin support SP_PERSIST_DATA + // Instantiate the PluginData class + m_plugin->m_plugin_data = new PluginData(m_storage); + // Load plugin data from storage layer + string pluginStoredData = m_plugin->m_plugin_data->loadStoredData(m_serviceName + m_name); + //call 'plugin_start' with plugin data: startData() + m_plugin->startData(pluginStoredData); + } + return true; +} + +/** + * Shutdown a pipeline element that is a filter. + * + * Remove registration for categories of interest, persist and plugin + * data that needs persisting and call shutdown on the plugin itself. + * + * @param serviceHandler The service handler of the service that is hostign the pipeline + * @param configHandler The config handler for the service from which we unregister + */ +void PipelineFilter::shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler) +{ + string filterCategoryName = m_serviceName + "_" + m_name; + configHandler->unregisterCategory(serviceHandler, filterCategoryName); + + // If plugin has SP_PERSIST_DATA option: + if (m_plugin->m_plugin_data) + { + // 1- call shutdownSaveData and get up-to-date plugin data. + string saveData = m_plugin->shutdownSaveData(); + // 2- store returned data: key is service/task categoryName + pluginName + string key(m_categoryName + m_plugin->getName()); + if (!m_plugin->m_plugin_data->persistPluginData(key, saveData)) + { + Logger::getLogger()->error("Filter plugin %s has failed to save data [%s] for key %s", + m_plugin->getName().c_str(), + saveData.c_str(), + key.c_str()); + } + } + else + { + // Call filter plugin shutdown + m_plugin->shutdown(); + } +} + +/** + * Reconfigure method + */ +void PipelineFilter::reconfigure(const string& newConfig) +{ + m_plugin->reconfigure(newConfig); +} diff --git a/C/common/pipeline_writer.cpp b/C/common/pipeline_writer.cpp new file mode 100644 index 0000000000..60fe784f71 --- /dev/null +++ b/C/common/pipeline_writer.cpp @@ -0,0 +1,55 @@ +/* + * Fledge pipeline writer class + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Mark Riddoch + */ +#include +#include +#include +#include +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +using namespace std; + +/** + * Ingest into a pipeline writer + */ +void PipelineWriter::ingest(READINGSET *readingSet) +{ +} + +/** + * Setup the pipeline writer + */ +bool PipelineWriter::setup(ManagementClient *mgmt, void *ingest, std::map& categories) +{ + return true; +} + +/** + * Initialise the pipeline writer + */ +bool PipelineWriter::init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output) +{ + return true; +} + +/** + * Shutdown the pipeline writer + */ +void PipelineWriter::shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler) +{ +} + +/** + * Return if the pipeline writer is ready to receive data + */ +bool PipelineWriter::isReady() +{ + return true; +} diff --git a/C/common/reading.cpp b/C/common/reading.cpp index c2b4a3bcb2..dbd3cf3e0a 100755 --- a/C/common/reading.cpp +++ b/C/common/reading.cpp @@ -35,7 +35,7 @@ std::vector Reading::m_dateTypes = { * Each actual datavalue that relates to that asset is held within an * instance of a Datapoint class. */ -Reading::Reading(const string& asset, Datapoint *value) : m_asset(asset), m_has_id(false) +Reading::Reading(const string& asset, Datapoint *value) : m_asset(asset), m_has_id(false), m_id(0) { m_values.push_back(value); // Store seconds and microseconds @@ -51,7 +51,7 @@ Reading::Reading(const string& asset, Datapoint *value) : m_asset(asset), m_has_ * Each actual datavalue that relates to that asset is held within an * instance of a Datapoint class. */ -Reading::Reading(const string& asset, vector values) : m_asset(asset), m_has_id(false) +Reading::Reading(const string& asset, vector values) : m_asset(asset), m_has_id(false), m_id(0) { for (auto it = values.cbegin(); it != values.cend(); it++) { @@ -70,7 +70,7 @@ Reading::Reading(const string& asset, vector values) : m_asset(asse * Each actual datavalue that relates to that asset is held within an * instance of a Datapoint class. */ -Reading::Reading(const string& asset, vector values, const string& ts) : m_asset(asset), m_has_id(false) +Reading::Reading(const string& asset, vector values, const string& ts) : m_asset(asset), m_has_id(false), m_id(0) { for (auto it = values.cbegin(); it != values.cend(); it++) { @@ -84,7 +84,7 @@ Reading::Reading(const string& asset, vector values, const string& /** * Construct a reading with datapoints given as JSON */ -Reading::Reading(const string& asset, const string& datapoints) : m_asset(asset), m_has_id(false) +Reading::Reading(const string& asset, const string& datapoints) : m_asset(asset), m_has_id(false), m_id(0) { Document d; if (d.Parse(datapoints.c_str()).HasParseError()) diff --git a/C/common/reading_set.cpp b/C/common/reading_set.cpp index 9033aeb742..1e7d7e6642 100755 --- a/C/common/reading_set.cpp +++ b/C/common/reading_set.cpp @@ -215,6 +215,9 @@ ReadingSet::append(vector& readings) /** * Deep copy a set of readings to this reading set. +* +* @param src The reading set to copy +* @return bool True if the reading set was copied */ bool ReadingSet::copy(const ReadingSet& src) @@ -226,14 +229,15 @@ ReadingSet::copy(const ReadingSet& src) // Iterate over all the readings in ReadingSet for (auto const &reading : src.getAllReadings()) { - std::string assetName = reading->getAssetName(); - std::vector dataPoints; + string assetName = reading->getAssetName(); + string ts = reading->getAssetDateUserTime(); + vector dataPoints; try { // Iterate over all the datapoints associated with one reading for (auto const &dp : reading->getReadingData()) { - std::string dataPointName = dp->getName(); + string dataPointName = dp->getName(); DatapointValue dv = dp->getData(); dataPoints.emplace_back(new Datapoint(dataPointName, dv)); @@ -263,7 +267,7 @@ ReadingSet::copy(const ReadingSet& src) throw; } - Reading *in = new Reading(assetName, dataPoints); + Reading *in = new Reading(assetName, dataPoints, ts); readings.emplace_back(in); } } diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index e44e6d1aed..f6b3c9a4d8 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -366,8 +366,8 @@ void DataLoad::bufferReadings(ReadingSet *readings) { if (m_pipeline) { - FilterPlugin *firstFilter = m_pipeline->getFirstFilterPlugin(); - if (firstFilter) + PipelineElement *firstElement = m_pipeline->getFirstFilterPlugin(); + if (firstElement) { // Check whether filters are set before calling ingest @@ -377,8 +377,11 @@ void DataLoad::bufferReadings(ReadingSet *readings) "filter pipeline is ready"); std::this_thread::sleep_for(std::chrono::milliseconds(150)); } + + m_pipeline->execute(); // Pass readingSet to filter chain - firstFilter->ingest(readings); + firstElement->ingest(readings); + m_pipeline->awaitCompletion(); return; } } @@ -566,8 +569,8 @@ bool DataLoad::loadFilters(const string& categoryName) void DataLoad::passToOnwardFilter(OUTPUT_HANDLE *outHandle, READINGSET *readingSet) { - // Get next filter in the pipeline - FilterPlugin *next = (FilterPlugin *)outHandle; + // Get next element in the pipeline + PipelineElement *next = (PipelineElement *)outHandle; // Pass readings to next filter next->ingest(readingSet); } @@ -626,6 +629,7 @@ void DataLoad::pipelineEnd(OUTPUT_HANDLE *outHandle, unique_lock lck(load->m_qMutex); load->m_queue.push_back(readingSet); load->m_fetchCV.notify_all(); + load->m_pipeline->completeBranch(); } /** diff --git a/C/services/south/include/ingest.h b/C/services/south/include/ingest.h index f9170eb5f2..47789fc469 100644 --- a/C/services/south/include/ingest.h +++ b/C/services/south/include/ingest.h @@ -160,6 +160,7 @@ class Ingest : public ServiceHandler { time_t m_deprecatedAgeOut; time_t m_deprecatedAgeOutStorage; PerformanceMonitor *m_performance; + std::mutex m_useDataMutex; }; #endif diff --git a/C/services/south/ingest.cpp b/C/services/south/ingest.cpp index 7b3822221f..45d8706de3 100755 --- a/C/services/south/ingest.cpp +++ b/C/services/south/ingest.cpp @@ -734,7 +734,7 @@ void Ingest::processQueue() lock_guard guard(m_pipelineMutex); if (m_filterPipeline && !m_filterPipeline->isShuttingDown()) { - FilterPlugin *firstFilter = m_filterPipeline->getFirstFilterPlugin(); + PipelineElement *firstFilter = m_filterPipeline->getFirstFilterPlugin(); if (firstFilter) { // Check whether filters are set before calling ingest @@ -744,12 +744,13 @@ void Ingest::processQueue() "filter pipeline is ready"); std::this_thread::sleep_for(std::chrono::milliseconds(150)); } - ReadingSet *readingSet = new ReadingSet(m_data); m_data->clear(); + m_filterPipeline->execute(); // Set the pipeline executing // Pass readingSet to filter chain firstFilter->ingest(readingSet); + m_filterPipeline->awaitCompletion(); /* * If filtering removed all the readings then simply clean up m_data and * return. @@ -1020,9 +1021,9 @@ void Ingest::passToOnwardFilter(OUTPUT_HANDLE *outHandle, READINGSET *readingSet) { // Get next filter in the pipeline - FilterPlugin *next = (FilterPlugin *)outHandle; + PipelineElement *next = (PipelineElement *)outHandle; - // Pass readings to next filter + // Pass readings to the next stage in the pipeline next->ingest(readingSet); } @@ -1050,34 +1051,16 @@ void Ingest::passToOnwardFilter(OUTPUT_HANDLE *outHandle, void Ingest::useFilteredData(OUTPUT_HANDLE *outHandle, READINGSET *readingSet) { - Ingest* ingest = (Ingest *)outHandle; - - if (ingest->m_data != readingSet->getAllReadingsPtr()) - { - if (ingest->m_data) - { - // Remove the readings in the vector - for(auto & rdngPtr : *(ingest->m_data)) - delete rdngPtr; - ingest->m_data->clear();// Remove any pointers still in the vector - delete ingest->m_data; - ingest->m_data = readingSet->moveAllReadings(); - } - else - { - // move reading vector to ingest - ingest->m_data = readingSet->moveAllReadings(); - } - } - else - { - Logger::getLogger()->info("%s:%d: Input readingSet modified by filter: ingest->m_data=%p, readingSet->getAllReadingsPtr()=%p", - __FUNCTION__, __LINE__, ingest->m_data, readingSet->getAllReadingsPtr()); - } + Ingest* ingest = (Ingest *)outHandle; + lock_guard guard(ingest->m_useDataMutex); + + vector *newData = readingSet->getAllReadingsPtr(); + ingest->m_data->insert(ingest->m_data->end(), newData->cbegin(), newData->cend()); readingSet->clear(); delete readingSet; + ingest->m_filterPipeline->completeBranch(); } /** diff --git a/C/tasks/north/sending_process/north_filter_pipeline.cpp b/C/tasks/north/sending_process/north_filter_pipeline.cpp index aabaa6c5f4..2b00b66e11 100644 --- a/C/tasks/north/sending_process/north_filter_pipeline.cpp +++ b/C/tasks/north/sending_process/north_filter_pipeline.cpp @@ -50,34 +50,18 @@ bool NorthFilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *u string errMsg = "'plugin_init' failed for filter '"; for (auto it = m_filters.begin(); it != m_filters.end(); ++it) { - string filterCategoryName = serviceName + "_" + (*it)->getName(); - ConfigCategory updatedCfg; - vector children; - - try - { - Logger::getLogger()->info("Load plugin categoryName %s", filterCategoryName.c_str()); - // Fetch up to date filter configuration - updatedCfg = mgtClient->getCategory(filterCategoryName); - // Add filter category name under service/process config name - children.push_back(filterCategoryName); - mgtClient->addChildCategories(serviceName, children); - - m_filterCategories[filterCategoryName] = (*it); - } - // TODO catch specific exceptions - catch (...) - { - throw; + if ((*it)->isBranch()) + { + PipelineBranch *branch = (PipelineBranch *)(*it); + branch->setFunctions(passToOnwardFilter, useFilteredData, _sendingProcess); } - + (*it)->setup(mgtClient, _sendingProcess, m_filterCategories); // Iterate the load filters set in the Ingest class m_filters member if ((it + 1) != m_filters.end()) { // Set next filter pointer as OUTPUT_HANDLE - if (!(*it)->init(updatedCfg, - (OUTPUT_HANDLE *)(*(it + 1)), + if (!(*it)->init((OUTPUT_HANDLE *)(*(it + 1)), filterReadingSetFn(passToOnwardFilter))) { errMsg += (*it)->getName() + "'"; @@ -92,8 +76,7 @@ bool NorthFilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *u const unsigned long* bufferIndex = sendingProcess->getLoadBufferIndexPtr(); // Set the Ingest class pointer as OUTPUT_HANDLE - if (!(*it)->init(updatedCfg, - (OUTPUT_HANDLE *)(bufferIndex), + if (!(*it)->init((OUTPUT_HANDLE *)(bufferIndex), filterReadingSetFn(useFilteredData))) { errMsg += (*it)->getName() + "'"; @@ -102,20 +85,6 @@ bool NorthFilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *u } } - if ((*it)->persistData()) - { - // Plugin support SP_PERSIST_DATA - // Instantiate the PluginData class - (*it)->m_plugin_data = new PluginData(&storage); - // Load plugin data from storage layer - string pluginStoredData = (*it)->m_plugin_data->loadStoredData(serviceName + (*it)->getName()); - //call 'plugin_start' with plugin data: startData() - (*it)->startData(pluginStoredData); - } - else - { - // We don't call simple plugin_start for filters right now - } } if (initErrors) diff --git a/C/tasks/north/sending_process/sending.cpp b/C/tasks/north/sending_process/sending.cpp index cf61b6633b..6782d78977 100755 --- a/C/tasks/north/sending_process/sending.cpp +++ b/C/tasks/north/sending_process/sending.cpp @@ -1003,7 +1003,7 @@ void SendingProcess::passToOnwardFilter(OUTPUT_HANDLE *outHandle, READINGSET *readings) { // Get next filter in the pipeline - FilterPlugin *next = (FilterPlugin *)outHandle; + PipelineElement *next = (PipelineElement *)outHandle; // Pass readings to next filter next->ingest(readings); } diff --git a/C/tasks/north/sending_process/sending_process.cpp b/C/tasks/north/sending_process/sending_process.cpp index 6ef59e8efb..3f3a86d4f6 100644 --- a/C/tasks/north/sending_process/sending_process.cpp +++ b/C/tasks/north/sending_process/sending_process.cpp @@ -119,13 +119,15 @@ void applyFilters(SendingProcess* loadData, ReadingSet* readingSet) { // Get first filter - FilterPlugin *firstFilter = loadData->filterPipeline->getFirstFilterPlugin(); + PipelineElement *firstFilter = loadData->filterPipeline->getFirstFilterPlugin(); // Call first filter "ingest" // Note: // next filters will be automatically called if (firstFilter) + { firstFilter->ingest(readingSet); + } } /** @@ -320,7 +322,7 @@ static void loadDataThread(SendingProcess *loadData) // Apply filters to the reading set if (loadData->filterPipeline) { - FilterPlugin *firstFilter = loadData->filterPipeline->getFirstFilterPlugin(); + PipelineElement *firstFilter = loadData->filterPipeline->getFirstFilterPlugin(); if (firstFilter) { diff --git a/C/thirdparty/rapidjson/example/simpledom/simpledom.cpp b/C/thirdparty/rapidjson/example/simpledom/simpledom.cpp index 80384199a9..0323bcc300 100644 --- a/C/thirdparty/rapidjson/example/simpledom/simpledom.cpp +++ b/C/thirdparty/rapidjson/example/simpledom/simpledom.cpp @@ -10,7 +10,7 @@ using namespace rapidjson; int main() { // 1. Parse a JSON string into DOM. - const char* json = "{\"project\":\"rapidjson\",\"stars\":10}"; + const char* json = "{\"project\":\"rapidjson\",\"stars\":10, \"test\" : \"null\"}"; Document d; d.Parse(json); @@ -25,5 +25,14 @@ int main() { // Output {"project":"rapidjson","stars":11} std::cout << buffer.GetString() << std::endl; + + if (d["test"].IsNull()) + { + std::cout << "Quotes have been stripped" << std::endl; + } + else if (d["test"].IsString()) + { + std::cout << "Quotes have not been stripped " << d["test"].GetString() << std::endl; + } return 0; } diff --git a/VERSION b/VERSION index 8accfb34ea..dc165f3307 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ fledge_version=2.5.0 -fledge_schema=73 +fledge_schema=74 diff --git a/python/fledge/common/service_record.py b/python/fledge/common/service_record.py index a6faca4e70..9cfc52df00 100644 --- a/python/fledge/common/service_record.py +++ b/python/fledge/common/service_record.py @@ -29,6 +29,7 @@ class Type(IntEnum): Northbound = 6 Dispatcher = 7 BucketStorage = 8 + Pipeline = 9 class Status(IntEnum): """Enumeration for Service Status""" diff --git a/python/fledge/services/core/api/service.py b/python/fledge/services/core/api/service.py index a9aa89819b..6330c7b511 100644 --- a/python/fledge/services/core/api/service.py +++ b/python/fledge/services/core/api/service.py @@ -231,6 +231,7 @@ async def add_service(request): curl -sX POST http://localhost:8081/fledge/service -d '{"name": "BucketServer", "type": "bucketstorage", "enabled": true}' | jq curl -X POST http://localhost:8081/fledge/service -d '{"name": "HTC", "plugin": "httpc", "type": "north", "enabled": true}' | jq curl -sX POST http://localhost:8081/fledge/service -d '{"name": "HT", "plugin": "http_north", "type": "north", "enabled": true, "config": {"verifySSL": {"value": "false"}}}' | jq + curl -sX POST http://localhost:8081/fledge/service -d '{"name": "Pipeline-Ingest", "type": "pipeline", "enabled": true}' | jq curl -sX POST http://localhost:8081/fledge/service?action=install -d '{"format":"repository", "name": "fledge-service-notification"}' curl -sX POST http://localhost:8081/fledge/service?action=install -d '{"format":"repository", "name": "fledge-service-dispatcher"}' @@ -326,13 +327,12 @@ async def add_service(request): raise web.HTTPBadRequest(reason='Missing type property in payload.') service_type = str(service_type).lower() - if service_type not in ['south', 'north', 'notification', 'management', 'dispatcher', 'bucketstorage']: - raise web.HTTPBadRequest(reason='Only south, north, notification, management, dispatcher and bucketstorage ' - 'types are supported.') - if plugin is None and service_type == 'south': - raise web.HTTPBadRequest(reason='Missing plugin property for type south in payload.') - if plugin is None and service_type == 'north': - raise web.HTTPBadRequest(reason='Missing plugin property for type north in payload.') + if service_type not in ['south', 'north', 'notification', 'management', 'dispatcher', + 'bucketstorage', 'pipeline']: + raise web.HTTPBadRequest(reason='Only south, north, notification, management, dispatcher, bucketstorage ' + 'and pipeline types are supported.') + if plugin is None and service_type in ('south', 'north'): + raise web.HTTPBadRequest(reason='Missing plugin property for type {} in payload.'.format(service_type)) if plugin and utils.check_reserved(plugin) is False: raise web.HTTPBadRequest(reason='Invalid plugin property in payload.') @@ -401,6 +401,9 @@ async def add_service(request): raise web.HTTPNotFound(reason=msg, body=json.dumps({"message": msg})) process_name = 'bucket_storage_c' script = '["services/bucket_storage_c"]' + elif service_type == 'pipeline': + process_name = 'pipeline_c' + script = '["services/pipeline_c"]' storage = connect.get_storage_async() config_mgr = ConfigurationManager(storage) diff --git a/python/fledge/services/core/scheduler/scheduler.py b/python/fledge/services/core/scheduler/scheduler.py index 37dffe19f5..7f30ac0a48 100644 --- a/python/fledge/services/core/scheduler/scheduler.py +++ b/python/fledge/services/core/scheduler/scheduler.py @@ -300,12 +300,14 @@ def _get_delay_in_sec(pname): val = 3 elif pname == 'notification_c': val = 5 - elif pname == 'south_c': + elif pname == 'pipeline_c': val = 7 - elif pname == 'north_C': + elif pname == 'south_c': val = 9 + elif pname == 'north_C': + val = 11 else: - val = 12 + val = 14 return val # This check is necessary only if significant time can elapse between "await" and diff --git a/scripts/fledge b/scripts/fledge index 8eb9fed76f..c226dceb04 100755 --- a/scripts/fledge +++ b/scripts/fledge @@ -526,6 +526,7 @@ fledge_status() { ps -ef | grep "fledge.services.notification" |grep -v python3| grep -v 'grep' | grep -v awk | awk '{printf "fledge.services.notification "; for(i=9;i<=NF;++i) printf $i FS; printf "\n"}' | sed -e 's/--token.*--name/--name/g' || true ps -ef | grep "fledge.services.dispatcher" |grep -v python3| grep -v 'grep' | grep -v awk | awk '{printf "fledge.services.dispatcher "; for(i=9;i<=NF;++i) printf $i FS; printf "\n"}' | sed -e 's/--token.*--name/--name/g' || true ps -ef | grep "fledge.services.bucket" |grep -v python3| grep -v 'grep' | grep -v awk | awk '{printf "fledge.services.bucket "; for(i=9;i<=NF;++i) printf $i FS; printf "\n"}' | sed -e 's/--token.*--name/--name/g' || true + ps -ef | grep "fledge.services.pipeline " |grep -v python3| grep -v 'grep' | grep -v awk | awk '{printf "fledge.services.pipeline "; for(i=9;i<=NF;++i) printf $i FS; printf "\n"}' | sed -e 's/--token.*--name/--name/g' || true # Show Python services (except core) ps -ef | grep -o 'python3 -m fledge.services.*' | grep -o 'fledge.services.*' | grep -v 'fledge.services.core' | grep -v 'fledge.services\.\*' | sed -e 's/--token.*--name/--name/g' || true diff --git a/scripts/plugins/storage/postgres/downgrade/73.sql b/scripts/plugins/storage/postgres/downgrade/73.sql new file mode 100644 index 0000000000..891fd5d221 --- /dev/null +++ b/scripts/plugins/storage/postgres/downgrade/73.sql @@ -0,0 +1 @@ +DELETE FROM fledge.scheduled_processes WHERE name = 'pipeline_c'; diff --git a/scripts/plugins/storage/postgres/init.sql b/scripts/plugins/storage/postgres/init.sql index f3938ab12a..0337c4e872 100644 --- a/scripts/plugins/storage/postgres/init.sql +++ b/scripts/plugins/storage/postgres/init.sql @@ -1086,6 +1086,7 @@ INSERT INTO fledge.scheduled_processes (name, script) VALUES ( 'nort INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'north_C', '["services/north_C"]', 200 ); INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'dispatcher_c', '["services/dispatcher_c"]', 20 ); INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'bucket_storage_c', '["services/bucket_storage_c"]', 10 ); +INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'pipeline_c', '["services/pipeline_c"]', 90 ); -- Automation script tasks -- diff --git a/scripts/plugins/storage/postgres/upgrade/74.sql b/scripts/plugins/storage/postgres/upgrade/74.sql new file mode 100644 index 0000000000..05274047c2 --- /dev/null +++ b/scripts/plugins/storage/postgres/upgrade/74.sql @@ -0,0 +1,2 @@ +-- Scheduled process entry for pipeline service +INSERT INTO fledge.scheduled_processes SELECT 'pipeline_c', '["services/pipeline_c"]', 90 WHERE NOT EXISTS (SELECT 1 FROM fledge.scheduled_processes WHERE name = 'pipeline_c'); diff --git a/scripts/plugins/storage/sqlite/downgrade/73.sql b/scripts/plugins/storage/sqlite/downgrade/73.sql new file mode 100644 index 0000000000..891fd5d221 --- /dev/null +++ b/scripts/plugins/storage/sqlite/downgrade/73.sql @@ -0,0 +1 @@ +DELETE FROM fledge.scheduled_processes WHERE name = 'pipeline_c'; diff --git a/scripts/plugins/storage/sqlite/init.sql b/scripts/plugins/storage/sqlite/init.sql index c04c30ac4a..cb35730643 100644 --- a/scripts/plugins/storage/sqlite/init.sql +++ b/scripts/plugins/storage/sqlite/init.sql @@ -842,6 +842,7 @@ INSERT INTO fledge.scheduled_processes (name, script) VALUES ( 'nort INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'north_C', '["services/north_C"]', 200 ); INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'dispatcher_c', '["services/dispatcher_c"]', 20 ); INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'bucket_storage_c', '["services/bucket_storage_c"]', 10 ); +INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'pipeline_c', '["services/pipeline_c"]', 90 ); -- Automation script tasks -- diff --git a/scripts/plugins/storage/sqlite/upgrade/74.sql b/scripts/plugins/storage/sqlite/upgrade/74.sql new file mode 100644 index 0000000000..05274047c2 --- /dev/null +++ b/scripts/plugins/storage/sqlite/upgrade/74.sql @@ -0,0 +1,2 @@ +-- Scheduled process entry for pipeline service +INSERT INTO fledge.scheduled_processes SELECT 'pipeline_c', '["services/pipeline_c"]', 90 WHERE NOT EXISTS (SELECT 1 FROM fledge.scheduled_processes WHERE name = 'pipeline_c'); diff --git a/scripts/plugins/storage/sqlitelb/downgrade/73.sql b/scripts/plugins/storage/sqlitelb/downgrade/73.sql new file mode 100644 index 0000000000..891fd5d221 --- /dev/null +++ b/scripts/plugins/storage/sqlitelb/downgrade/73.sql @@ -0,0 +1 @@ +DELETE FROM fledge.scheduled_processes WHERE name = 'pipeline_c'; diff --git a/scripts/plugins/storage/sqlitelb/init.sql b/scripts/plugins/storage/sqlitelb/init.sql index 417948f440..48b7771f85 100644 --- a/scripts/plugins/storage/sqlitelb/init.sql +++ b/scripts/plugins/storage/sqlitelb/init.sql @@ -842,6 +842,8 @@ INSERT INTO fledge.scheduled_processes (name, script) VALUES ( 'nort INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'north_C', '["services/north_C"]', 200 ); INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'dispatcher_c', '["services/dispatcher_c"]', 20 ); INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'bucket_storage_c', '["services/bucket_storage_c"]', 10 ); +INSERT INTO fledge.scheduled_processes (name, script, priority) VALUES ( 'pipeline_c', '["services/pipeline_c"]', 90 ); + -- Automation script tasks -- diff --git a/scripts/plugins/storage/sqlitelb/upgrade/74.sql b/scripts/plugins/storage/sqlitelb/upgrade/74.sql new file mode 100644 index 0000000000..05274047c2 --- /dev/null +++ b/scripts/plugins/storage/sqlitelb/upgrade/74.sql @@ -0,0 +1,2 @@ +-- Scheduled process entry for pipeline service +INSERT INTO fledge.scheduled_processes SELECT 'pipeline_c', '["services/pipeline_c"]', 90 WHERE NOT EXISTS (SELECT 1 FROM fledge.scheduled_processes WHERE name = 'pipeline_c'); diff --git a/scripts/services/pipeline_c b/scripts/services/pipeline_c new file mode 100755 index 0000000000..3af034d328 --- /dev/null +++ b/scripts/services/pipeline_c @@ -0,0 +1,29 @@ +#!/bin/bash +# Run a Fledge pipeline service written in C/C++ +if [ "${FLEDGE_ROOT}" = "" ]; then + FLEDGE_ROOT=/usr/local/fledge +fi + +if [ ! -d "${FLEDGE_ROOT}" ]; then + logger "Fledge home directory missing or incorrectly set environment." + exit 1 +fi + + +# startup with delay +delay() { + for ARG in "$@"; + do + PARAM=$(echo $ARG | cut -f1 -d=) + if [ $PARAM = '--delay' ]; then + PARAM_LENGTH=${#PARAM} + VALUE="${ARG:$PARAM_LENGTH+1}" + sleep $VALUE + break + fi + done +} + +cd "${FLEDGE_ROOT}/services" +delay "$@" +./fledge.services.pipeline "$@" diff --git a/tests/unit/python/fledge/common/test_service_record.py b/tests/unit/python/fledge/common/test_service_record.py index 8034eaf5a7..33489c40ad 100644 --- a/tests/unit/python/fledge/common/test_service_record.py +++ b/tests/unit/python/fledge/common/test_service_record.py @@ -20,6 +20,14 @@ @pytest.allure.story("common", "service-record") class TestServiceRecord: + @pytest.mark.parametrize("name, value", [ + ('Storage', 1), ('Core', 2), ('Southbound', 3), ('Notification', 4), ('Management', 5), ('Northbound', 6), + ('Dispatcher', 7), ('BucketStorage', 8), ('Pipeline', 9) + ]) + def test_types(self, name, value): + assert 9 == len(ServiceRecord.Type) + assert name == ServiceRecord.Type(value).name + @pytest.mark.parametrize("s_port", [None, 12, "34"]) def test_init(self, s_port): obj = ServiceRecord("some id", "aName", "Storage", "http", "127.0.0.1", s_port, 1234) @@ -34,7 +42,8 @@ def test_init(self, s_port): assert 1 == obj._status @pytest.mark.parametrize("s_type", [ - "Storage", "Core", "Southbound", "Notification", "Management", "Northbound", "Dispatcher", "BucketStorage"]) + "Storage", "Core", "Southbound", "Notification", "Management", "Northbound", "Dispatcher", + "BucketStorage", "Pipeline"]) def test_init_with_valid_type(self, s_type): obj = ServiceRecord("some id", "aName", s_type, "http", "127.0.0.1", None, 1234) assert "some id" == obj._id @@ -42,6 +51,6 @@ def test_init_with_valid_type(self, s_type): assert s_type == obj._type def test_init_with_invalid_type(self): - with pytest.raises(Exception) as excinfo: - obj = ServiceRecord("some id", "aName", "BLAH", "http", "127.0.0.1", None, 1234) - assert excinfo.type is ServiceRecord.InvalidServiceType + with pytest.raises(Exception) as ex: + ServiceRecord("some id", "aName", "BLAH", "http", "127.0.0.1", None, 1234) + assert ex.type is ServiceRecord.InvalidServiceType diff --git a/tests/unit/python/fledge/services/core/api/test_service.py b/tests/unit/python/fledge/services/core/api/test_service.py index feaf4fa4b3..9e2f0f1879 100644 --- a/tests/unit/python/fledge/services/core/api/test_service.py +++ b/tests/unit/python/fledge/services/core/api/test_service.py @@ -227,7 +227,7 @@ async def test_get_service_with_type(self, mocker, client, _type, svc_record_cou 'Only "true", "false", true, false are allowed for value of enabled.'), ('{"name": "test", "plugin": "dht11"}', 400, "Missing type property in payload."), ('{"name": "test", "plugin": "dht11", "type": "blah"}', 400, - "Only south, north, notification, management, dispatcher and bucketstorage types are supported."), + "Only south, north, notification, management, dispatcher, bucketstorage and pipeline types are supported."), ('{"name": "test", "type": "south"}', 400, "Missing plugin property for type south in payload.") ]) async def test_add_service_with_bad_params(self, client, code, payload, message): From b379b962d184bdbcef8cb1af3a92e88d5817aa94 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 22 Jul 2024 15:46:20 +0530 Subject: [PATCH 32/91] common utility function added to find out the differences between two python dictionaries Signed-off-by: ashish-jabble --- python/fledge/common/utils.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/python/fledge/common/utils.py b/python/fledge/common/utils.py index f11cb409da..784db0cd4b 100644 --- a/python/fledge/common/utils.py +++ b/python/fledge/common/utils.py @@ -148,3 +148,35 @@ def wrapper(*args, **kwargs): return wrapper + +def dict_difference(dict1, dict2): + """ Compare two dictionaries and return their difference """ + diff = {} + + # Check keys in dict1 not in dict2 + for key in dict1: + if key not in dict2: + diff[key] = dict1[key] + else: + # Recursively compare nested dictionaries + if isinstance(dict1[key], dict) and isinstance(dict2[key], dict): + nested_diff = dict_difference(dict1[key], dict2[key]) + if nested_diff: + diff[key] = nested_diff + elif dict1[key] != dict2[key]: + diff[key] = dict1[key] + + # Check keys in dict2 not in dict1 + for key in dict2: + if key not in dict1: + diff[key] = dict2[key] + else: + # Recursively compare nested dictionaries + if isinstance(dict1[key], dict) and isinstance(dict2[key], dict): + nested_diff = dict_difference(dict1[key], dict2[key]) + if nested_diff: + diff[key] = nested_diff + elif dict1[key] != dict2[key]: + diff[key] = dict2[key] + return diff + From b90e91d4afa84bcb777989c54a582c2e8c7c3ce5 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 22 Jul 2024 15:47:17 +0530 Subject: [PATCH 33/91] CONCH audit log entry fixes on the basis of python dict difference Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index f705fba746..bce4906824 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -17,6 +17,7 @@ import ast import aiohttp.web_request +from fledge.common import utils as common_utils from fledge.common.storage_client.payload_builder import PayloadBuilder from fledge.common.storage_client.storage_client import StorageClientAsync from fledge.common.storage_client.exceptions import StorageServerError @@ -1421,7 +1422,7 @@ async def create_category(self, category_name, category_value, category_descript else: await self._update_category(category_name, category_val_prepared, category_description, display_name) - diff = set(category_val_prepared) - set(category_val_storage) + diff = common_utils.dict_difference(category_val_prepared, category_val_storage) if diff: audit = AuditLogger(self._storage) audit_details = { From 80fcfca0ee6b5ef7d5071e7f14d53c718ec592c8 Mon Sep 17 00:00:00 2001 From: Himanshu Vimal <67678828+cyberwalk3r@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:01:06 +0530 Subject: [PATCH 34/91] FOGL-8913: Fix readings count getting doubled on restart. (#1430) - Reverting changes done in FOGL-8285. Signed-off-by: Himanshu Vimal --- C/services/north/data_load.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index f6b3c9a4d8..3cbf889a7d 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -105,18 +105,12 @@ void DataLoad::restart() */ bool DataLoad::setDataSource(const string& source) { - if (source.compare("statistics") == 0) { + if (source.compare("statistics") == 0) m_dataSource = SourceStatistics; - m_lastFetched = 0; // Reset on source change - } - else if (source.compare("readings") == 0) { + else if (source.compare("readings") == 0) m_dataSource = SourceReadings; - m_lastFetched = 0; // Reset on source change - } - else if (source.compare("audit") == 0) { + else if (source.compare("audit") == 0) m_dataSource = SourceAudit; - m_lastFetched = 0; // Reset on source change - } else { Logger::getLogger()->error("Unsupported source '%s' for north service '%s'", From cf8d2ffc7bc899b30690ba9d887e0fe16db62c8c Mon Sep 17 00:00:00 2001 From: Himanshu Vimal <67678828+cyberwalk3r@users.noreply.github.com> Date: Wed, 31 Jul 2024 19:26:45 +0530 Subject: [PATCH 35/91] FOGL-8947: Last send Id not updated for single send cycle. (#1431) * FOGL-8947: Last send Id not updated for single send cycle. - Update last sent id if no readings to send. Signed-off-by: Himanshu Vimal --- C/services/north/data_load.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index 3cbf889a7d..15a6ce84ee 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -41,6 +41,7 @@ DataLoad::DataLoad(const string& name, long streamId, StorageClient *storage) : m_nextStreamUpdate = 1; m_streamUpdate = 1; m_lastFetched = getLastSentId(); + m_streamSent = getLastSentId(); m_flushRequired = false; m_thread = new thread(threadMain, this); loadFilters(name); @@ -174,6 +175,8 @@ void DataLoad::triggerRead(unsigned int blockSize) void DataLoad::readBlock(unsigned int blockSize) { int n_waits = 0; + int n_update_streamId = 0; + int max_wait_count = 5; // Maximum wait counter to update streams table unsigned int waitPeriod = INITIAL_BLOCK_WAIT; do { @@ -211,6 +214,7 @@ void DataLoad::readBlock(unsigned int blockSize) } if (readings && readings->getCount()) { + n_update_streamId = 0; m_lastFetched = readings->getLastId(); Logger::getLogger()->debug("DataLoad::readBlock(): Got %lu readings from storage client, updated m_lastFetched=%lu", readings->getCount(), m_lastFetched); @@ -226,6 +230,13 @@ void DataLoad::readBlock(unsigned int blockSize) { // Delete the empty readings set delete readings; + n_update_streamId++; + if (n_update_streamId > max_wait_count) { + // Update 'last_object_id' in 'streams' table when no readings to send + n_update_streamId = 0; + m_streamSent = getLastFetched(); + flushLastSentId(); + } } else { @@ -444,7 +455,7 @@ InsertValues streamValues; if (m_storage->insertTable("streams", streamValues) != 1) { Logger::getLogger()->error("Failed to insert a row into the streams table"); - } + } else { // Select the row just created, having description='process name' From f0ae2914919592c01e09de0896f060a521357dce Mon Sep 17 00:00:00 2001 From: pintomax Date: Wed, 31 Jul 2024 17:13:33 +0200 Subject: [PATCH 36/91] FOGL-8955: fix check of updateTable return value (#1433) FOGL-8955: fix check of updateTable return value --- C/services/north/data_send.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/C/services/north/data_send.cpp b/C/services/north/data_send.cpp index 4e0f5048e3..4c4fca4311 100755 --- a/C/services/north/data_send.cpp +++ b/C/services/north/data_send.cpp @@ -361,7 +361,7 @@ void DataSender::flushStatistics() int rv = m_loader->getStorage()->updateTable("statistics", statsUpdates); // Check for errors - if (rv != statsData.size()) + if (rv < 1) { if (++m_statsUpdateFails > STATS_UPDATE_FAIL_THRESHOLD) { From db18343006830a9bde665a9ceb7468d4aabef982 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 1 Aug 2024 19:10:54 +0530 Subject: [PATCH 37/91] more restrictions added in update password API endpoint Signed-off-by: ashish-jabble --- python/fledge/services/core/api/auth.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index e010260d27..dc1cccdd4d 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -576,6 +576,16 @@ async def update_password(request): msg = "User ID should be in integer." raise web.HTTPBadRequest(reason=msg, body=json.dumps({"message": msg})) + # Restrictions + if int(request.user["id"]) != int(user_id): + # Super Admin default user + if int(user_id) == 1: + raise web.HTTPUnauthorized(reason="Insufficient privileges to update the password for the given user.") + else: + if int(request.user["role_id"]) != ADMIN_ROLE_ID: + raise web.HTTPUnauthorized( + reason="Insufficient privileges to update the password for the given user.") + data = await request.json() current_password = data.get('current_password') new_password = data.get('new_password') From 128e09c017336a9686548d8ddc434b700c89bfaa Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 1 Aug 2024 19:11:03 +0530 Subject: [PATCH 38/91] unit tests updated Signed-off-by: ashish-jabble --- .../services/core/api/test_auth_mandatory.py | 144 +++++++++++------- 1 file changed, 90 insertions(+), 54 deletions(-) diff --git a/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py b/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py index 32d06afd11..c556c049b2 100644 --- a/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py +++ b/tests/unit/python/fledge/services/core/api/test_auth_mandatory.py @@ -532,23 +532,44 @@ async def test_update_user(self, client, mocker, payload, exp_result): ({"current_password": "F0gl@mp", "new_password": "FL"}, PASSWORD_MIN_LENGTH_ERROR_MSG), ({"current_password": "F0gl@mp", "new_password": 1}, "New password should be a valid string.") ]) - async def test_update_password_with_bad_data(self, client, request_data, msg): + async def test_update_password_with_bad_data(self, client, mocker, request_data, msg): uid = 2 + patch_logger_debug, patch_validate_token, patch_refresh_token, patch_user_get = await self.auth_token_fixture( + mocker, is_admin=False) rv1 = await mock_coro(msg) if sys.version_info.major == 3 and sys.version_info.minor >= 8 else ( asyncio.ensure_future(mock_coro(msg))) - with patch.object(middleware._logger, 'debug') as patch_logger_debug: - with patch.object(auth, 'validate_password', return_value=rv1): - resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data)) - assert 400 == resp.status - assert msg == resp.reason + with patch.object(auth, 'validate_password', return_value=rv1): + resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data), + headers=NORMAL_USER_HEADER) + assert 400 == resp.status + assert msg == resp.reason patch_logger_debug.assert_called_once_with('Received %s request for %s', 'PUT', '/fledge/user/{}/password'.format(uid)) + patch_validate_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_refresh_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_user_get.assert_called_once_with(uid=uid) - async def test_update_password_with_invalid_current_password(self, client): + async def test_update_password_with_other_user(self, client, mocker): + uid = 1 + patch_logger_debug, patch_validate_token, patch_refresh_token, patch_user_get = await self.auth_token_fixture( + mocker, is_admin=False) + resp = await client.put('/fledge/user/{}/password'.format(uid), + data=json.dumps({"current_password": "fledge", "new_password": "newfledge"}), + headers=NORMAL_USER_HEADER) + assert 401 == resp.status + assert "Insufficient privileges to update the password for the given user." == resp.reason + patch_logger_debug.assert_called_once_with('Received %s request for %s', 'PUT', + '/fledge/user/{}/password'.format(uid)) + patch_validate_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_refresh_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_user_get.assert_called_once_with(uid=2) + + async def test_update_password_with_invalid_current_password(self, client, mocker): request_data = {"current_password": "blah", "new_password": "F0gl@mp"} uid = 2 msg = 'Invalid current password.' - + patch_logger_debug, patch_validate_token, patch_refresh_token, patch_user_get = await self.auth_token_fixture( + mocker, is_admin=False) # Changed in version 3.8: patch() now returns an AsyncMock if the target is an async function. if sys.version_info >= (3, 8): rv1 = await mock_coro("") @@ -556,24 +577,29 @@ async def test_update_password_with_invalid_current_password(self, client): else: rv1 = asyncio.ensure_future(mock_coro("")) rv2 = asyncio.ensure_future(mock_coro(None)) - with patch.object(middleware._logger, 'debug') as patch_logger_debug: - with patch.object(auth, 'validate_password', return_value=rv1): - with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: - resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data)) - assert 404 == resp.status - assert msg == resp.reason - patch_user_exists.assert_called_once_with(str(uid), request_data['current_password']) + with patch.object(auth, 'validate_password', return_value=rv1): + with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: + resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data), + headers=NORMAL_USER_HEADER) + assert 404 == resp.status + assert msg == resp.reason + patch_user_exists.assert_called_once_with(str(uid), request_data['current_password']) patch_logger_debug.assert_called_once_with('Received %s request for %s', 'PUT', '/fledge/user/{}/password'.format(uid)) + patch_validate_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_refresh_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_user_get.assert_called_once_with(uid=uid) @pytest.mark.parametrize("exception_name, status_code, msg", [ (ValueError, 400, 'None'), (User.DoesNotExist, 404, 'User with ID:<2> does not exist.'), (User.PasswordAlreadyUsed, 400, 'The new password should be different from previous 3 used.') ]) - async def test_update_password_exceptions(self, client, exception_name, status_code, msg): + async def test_update_password_exceptions(self, client, mocker, exception_name, status_code, msg): request_data = {"current_password": "fledge", "new_password": "F0gl@mp"} uid = 2 + patch_logger_debug, patch_validate_token, patch_refresh_token, patch_user_get = await self.auth_token_fixture( + mocker, is_admin=False) # Changed in version 3.8: patch() now returns an AsyncMock if the target is an async function. if sys.version_info >= (3, 8): rv1 = await mock_coro("") @@ -581,23 +607,28 @@ async def test_update_password_exceptions(self, client, exception_name, status_c else: rv1 = asyncio.ensure_future(mock_coro("")) rv2 = asyncio.ensure_future(mock_coro(uid)) - with patch.object(middleware._logger, 'debug') as patch_logger_debug: - with patch.object(auth, 'validate_password', return_value=rv1): - with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: - with patch.object(User.Objects, 'update', side_effect=exception_name(msg)) as patch_update: - resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data)) - assert status_code == resp.status - assert msg == resp.reason - patch_update.assert_called_once_with(2, {'password': request_data['new_password']}) - patch_user_exists.assert_called_once_with(str(uid), request_data['current_password']) + with patch.object(auth, 'validate_password', return_value=rv1): + with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: + with patch.object(User.Objects, 'update', side_effect=exception_name(msg)) as patch_update: + resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data), + headers=NORMAL_USER_HEADER) + assert status_code == resp.status + assert msg == resp.reason + patch_update.assert_called_once_with(2, {'password': request_data['new_password']}) + patch_user_exists.assert_called_once_with(str(uid), request_data['current_password']) patch_logger_debug.assert_called_once_with('Received %s request for %s', 'PUT', '/fledge/user/{}/password'.format(uid)) + patch_validate_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_refresh_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_user_get.assert_called_once_with(uid=uid) - async def test_update_password_unknown_exception(self, client): + async def test_update_password_unknown_exception(self, client, mocker): request_data = {"current_password": "fledge", "new_password": "F0gl@mp"} uid = 2 msg = 'Something went wrong' logger_msg = 'Failed to update the user ID:<{}>.'.format(uid) + patch_logger_debug, patch_validate_token, patch_refresh_token, patch_user_get = await self.auth_token_fixture( + mocker, is_admin=False) # Changed in version 3.8: patch() now returns an AsyncMock if the target is an async function. if sys.version_info >= (3, 8): rv1 = await mock_coro("") @@ -605,28 +636,31 @@ async def test_update_password_unknown_exception(self, client): else: rv1 = asyncio.ensure_future(mock_coro("")) rv2 = asyncio.ensure_future(mock_coro(uid)) - with patch.object(middleware._logger, 'debug') as patch_logger_debug: - with patch.object(auth, 'validate_password', return_value=rv1): - with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: - with patch.object(User.Objects, 'update', side_effect=Exception(msg)) as patch_update: - with patch.object(auth._logger, 'error') as patch_logger: - resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data)) - assert 500 == resp.status - assert msg == resp.reason - args = patch_logger.call_args - assert logger_msg == args[0][1] - patch_update.assert_called_once_with(2, {'password': request_data['new_password']}) - patch_user_exists.assert_called_once_with(str(uid), request_data['current_password']) + with patch.object(auth, 'validate_password', return_value=rv1): + with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: + with patch.object(User.Objects, 'update', side_effect=Exception(msg)) as patch_update: + with patch.object(auth._logger, 'error') as patch_logger: + resp = await client.put('/fledge/user/{}/password'.format(uid), data=json.dumps(request_data), + headers=NORMAL_USER_HEADER) + assert 500 == resp.status + assert msg == resp.reason + args = patch_logger.call_args + assert logger_msg == args[0][1] + patch_update.assert_called_once_with(2, {'password': request_data['new_password']}) + patch_user_exists.assert_called_once_with(str(uid), request_data['current_password']) patch_logger_debug.assert_called_once_with('Received %s request for %s', 'PUT', '/fledge/user/{}/password'.format(uid)) + patch_validate_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_refresh_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_user_get.assert_called_once_with(uid=uid) - async def test_update_password(self, client): + async def test_update_password(self, client, mocker): request_data = {"current_password": "fledge", "new_password": "F0gl@mp"} ret_val = {'response': 'updated', 'rows_affected': 1} - uname = 'aj' user_id = 2 msg = "Password has been updated successfully for user ID:<{}>.".format(user_id) - + patch_logger_debug, patch_validate_token, patch_refresh_token, patch_user_get = await self.auth_token_fixture( + mocker, is_admin=False) # Changed in version 3.8: patch() now returns an AsyncMock if the target is an async function. if sys.version_info.major == 3 and sys.version_info.minor >= 8: rv1 = await mock_coro("") @@ -637,21 +671,23 @@ async def test_update_password(self, client): rv2 = asyncio.ensure_future(mock_coro(user_id)) rv3 = asyncio.ensure_future(mock_coro(ret_val)) - with patch.object(middleware._logger, 'debug') as patch_logger_debug: - with patch.object(auth, 'validate_password', return_value=rv1): - with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: - with patch.object(User.Objects, 'update', return_value=rv3) as patch_update: - with patch.object(auth._logger, 'info') as patch_auth_logger_info: - resp = await client.put('/fledge/user/{}/password'.format(user_id), - data=json.dumps(request_data)) - assert 200 == resp.status - r = await resp.text() - assert {'message': msg} == json.loads(r) - patch_auth_logger_info.assert_called_once_with(msg) - patch_update.assert_called_once_with(user_id, {'password': request_data['new_password']}) - patch_user_exists.assert_called_once_with(str(user_id), request_data['current_password']) + with patch.object(auth, 'validate_password', return_value=rv1): + with patch.object(User.Objects, 'is_user_exists', return_value=rv2) as patch_user_exists: + with patch.object(User.Objects, 'update', return_value=rv3) as patch_update: + with patch.object(auth._logger, 'info') as patch_auth_logger_info: + resp = await client.put('/fledge/user/{}/password'.format(user_id), + data=json.dumps(request_data), headers=NORMAL_USER_HEADER) + assert 200 == resp.status + r = await resp.text() + assert {'message': msg} == json.loads(r) + patch_auth_logger_info.assert_called_once_with(msg) + patch_update.assert_called_once_with(user_id, {'password': request_data['new_password']}) + patch_user_exists.assert_called_once_with(str(user_id), request_data['current_password']) patch_logger_debug.assert_called_once_with('Received %s request for %s', 'PUT', '/fledge/user/{}/password'.format(user_id)) + patch_validate_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_refresh_token.assert_called_once_with(NORMAL_USER_HEADER['Authorization']) + patch_user_get.assert_called_once_with(uid=user_id) @pytest.mark.parametrize("request_data", ['blah', '123blah']) async def test_delete_bad_user(self, client, mocker, request_data): From a613c20de4335a953caa2eabaac4b9e3222f58a1 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 1 Aug 2024 19:11:15 +0530 Subject: [PATCH 39/91] API integration tests updated Signed-off-by: ashish-jabble --- .../python/api/test_endpoints_with_different_user_types.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/system/python/api/test_endpoints_with_different_user_types.py b/tests/system/python/api/test_endpoints_with_different_user_types.py index 6542f8eaf0..4a3882be72 100644 --- a/tests/system/python/api/test_endpoints_with_different_user_types.py +++ b/tests/system/python/api/test_endpoints_with_different_user_types.py @@ -415,7 +415,8 @@ class TestAPIEndpointsWithControlUserType: def test_login(self, fledge_url, wait_time): time.sleep(wait_time * 2) conn = http.client.HTTPConnection(fledge_url) - conn.request("POST", "/fledge/login", json.dumps({"username": CONTROL_USERNAME, "password": CONTROL_PWD})) + conn.request("POST", "/fledge/login", json.dumps({"username": CONTROL_USERNAME, + "password": CONTROL_PWD})) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -436,7 +437,7 @@ def test_login(self, fledge_url, wait_time): ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 200), ("GET", "/fledge/user?id={}&username={}".format(5, CONTROL_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 5), 200), - ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 500), ("PUT", "/fledge/user/3/password", 500), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 401), ("PUT", "/fledge/user/5/password", 500), ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 500), ("PUT", "/fledge/31/logout", 401), From ea328b8ef623edc233b5014f0237e8c781d16e35 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 5 Aug 2024 19:40:17 +0530 Subject: [PATCH 40/91] Firewall IP address class addition Signed-off-by: ashish-jabble --- python/fledge/services/core/firewall.py | 86 +++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 python/fledge/services/core/firewall.py diff --git a/python/fledge/services/core/firewall.py b/python/fledge/services/core/firewall.py new file mode 100644 index 0000000000..ada60fb89c --- /dev/null +++ b/python/fledge/services/core/firewall.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +# FLEDGE_BEGIN +# See: http://fledge-iot.readthedocs.io/ +# FLEDGE_END + +import json +from fledge.common.logger import FLCoreLogger + +__author__ = "Ashish Jabble" +__copyright__ = "Copyright (c) 2024, Dianomic Systems Inc." +__license__ = "Apache 2.0" +__version__ = "${VERSION}" + +_logger = FLCoreLogger().get_logger(__name__) + +DEFAULT_CONFIG = { + 'allowList': { + 'description': 'A list of allowed IP addresses', + 'type': 'list', + 'items': 'string', + 'default': "[]", + 'displayName': 'Allowed IP Addresses', + 'order': '1', + 'permissions': ['admin'] + }, + 'denyList': { + 'description': 'A list of denied IP addresses', + 'type': 'list', + 'items': 'string', + 'default': "[]", + 'displayName': 'Denied IP Addresses', + 'order': '2', + 'permissions': ['admin'] + } + } + + +class Firewall: + + __slots__ = ['cat_name', 'display_name', 'description', 'config'] + + + def __init__(self): + self.cat_name = 'firewall' + self.display_name = 'Firewall' + self.description = 'Allow and Denied list of IP addresses for Firewall' + self.config = DEFAULT_CONFIG + + + def __repr__(self): + template = ('Firewall info: ') + return template.format(s=self) + + + def __str__(self): + return self.__repr__() + + + class IPList: + + + @classmethod + def get(cls) -> dict: + # To avoid cyclic import + from fledge.services.core import server + return server.Server._firewall_ip_addresses + + + @classmethod + def save(cls, data: dict) -> None: + # To avoid cyclic import + from fledge.services.core import server + if 'allowList' in data: + server.Server._firewall_ip_addresses.update({'allowList': json.loads(data['allowList']['value'])}) + if 'denyList' in data: + server.Server._firewall_ip_addresses.update({'denyList': json.loads(data['denyList']['value'])}) + + + @classmethod + def clear(cls) -> None: + # To avoid cyclic import + from fledge.services.core import server + server.Server._firewall_ip_addresses = {} + From cf262d1714fb79d9fb96a2efccdf06d7a46e2c7a Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 5 Aug 2024 19:41:58 +0530 Subject: [PATCH 41/91] firewall category addition and be a child of rest_api category Signed-off-by: ashish-jabble --- python/fledge/services/core/server.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index e61a01b530..8f16c97b74 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -50,6 +50,7 @@ from fledge.common.web.ssl_wrapper import SSLVerifier from fledge.services.core.api import exceptions as api_exception from fledge.services.core.api.control_service import acl_management as acl_management +from fledge.services.core import firewall __author__ = "Amarendra K. Sinha, Praveen Garg, Terris Linenbach, Massimiliano Pinto, Ashish Jabble" @@ -362,6 +363,9 @@ class Server: _user_idle_session_timeout = 15 * 60 """ User idle session timeout (in minutes) """ + _firewall_ip_addresses = {} + """ IP addresses for firewall check """ + _INSTALLATION_DEFAULT_CONFIG = { 'maxUpdate': { 'description': 'Maximum updates per day', @@ -563,6 +567,21 @@ async def password_config(cls): raise + @classmethod + async def firewall_config(cls): + try: + obj = firewall.Firewall() + category = obj.cat_name + await cls._configuration_manager.create_category(category, obj.config, obj.description, True, + display_name=obj.display_name) + config = await cls._configuration_manager.get_category_all_items(category) + firewall.Firewall.IPList.save(data=config) + await cls._configuration_manager.create_child_category("rest_api", [category]) + except Exception as ex: + _logger.exception(ex) + raise + + @classmethod async def service_config(cls): """ @@ -968,8 +987,11 @@ def _start_core(cls, loop=None): # start monitor loop.run_until_complete(cls._start_service_monitor()) + # REST API loop.run_until_complete(cls.rest_api_config()) loop.run_until_complete(cls.password_config()) + loop.run_until_complete(cls.firewall_config()) + cls.service_app = cls._make_app(auth_required=cls.is_auth_required, auth_method=cls.auth_method) # ssl context From dce0a83f17a22c285a8576a0f3e537b962f1028a Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 5 Aug 2024 19:43:07 +0530 Subject: [PATCH 42/91] validation of firewall IP check in middleware and reject if does not satisfy the condition Signed-off-by: ashish-jabble --- python/fledge/common/web/middleware.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index aa470b98fb..150609d233 100644 --- a/python/fledge/common/web/middleware.py +++ b/python/fledge/common/web/middleware.py @@ -13,8 +13,10 @@ from aiohttp import web import jwt -from fledge.services.core.user_model import User from fledge.common.logger import FLCoreLogger +from fledge.services.core import firewall +from fledge.services.core.user_model import User + __author__ = "Praveen Garg" __copyright__ = "Copyright (c) 2017 OSIsoft, LLC" @@ -46,6 +48,7 @@ async def middleware(request): _logger.debug("Received %s request for %s", request.method, request.path) request.is_auth_optional = True request.user = None + check_firewall_ip(request) return await handler(request) return middleware @@ -68,10 +71,9 @@ async def middleware(request): # request must carry auth header, # actual value will be checked too and if bad then 401: unauthorized will be returned _logger.debug("Received %s request for %s", request.method, request.path) - request.is_auth_optional = False request.user = None - + check_firewall_ip(request) if request.method == 'OPTIONS': return await handler(request) @@ -231,3 +233,18 @@ async def validate_requests(request): else: raise web.HTTPForbidden + +def check_firewall_ip(req: web.Request) -> None: + source_ip_address = req.transport.get_extra_info('peername')[0] + if source_ip_address not in ['localhost', '127.0.0.1']: + firewall_ip_addresses = firewall.Firewall.IPList.get() + if 'allowList' in firewall_ip_addresses: + allowed = firewall_ip_addresses['allowList'] + if allowed: + if source_ip_address not in allowed: + raise web.HTTPForbidden + else: + if 'denyList' in firewall_ip_addresses: + if source_ip_address in firewall_ip_addresses['denyList']: + raise web.HTTPForbidden + From 70737742298010e4826d13fe680c89ce072d9fe0 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 5 Aug 2024 19:43:38 +0530 Subject: [PATCH 43/91] special handling of firewall category updates in core configuration manager Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index bce4906824..d3fdc1f677 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -2082,6 +2082,9 @@ def _handle_config_items(self, cat_name: str, cat_value: dict) -> None: if cat_name == 'CONFIGURATION': if 'cacheSize' in cat_value: self._cacheManager.max_cache_size = int(cat_value['cacheSize']['value']) + elif cat_name == 'firewall': + from fledge.services.core import firewall + firewall.Firewall.IPList.save(data=cat_value) async def _check_updates_by_role(self, request: aiohttp.web_request.Request) -> str: async def get_role_name(): From 95ee8db01b2c365bbeb72fa9dc686c883e0f6723 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 6 Aug 2024 11:35:15 +0530 Subject: [PATCH 44/91] API integration tests updated as per new firewall category Signed-off-by: ashish-jabble --- tests/system/python/api/test_audit.py | 12 ++++++------ tests/system/python/api/test_configuration.py | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/system/python/api/test_audit.py b/tests/system/python/api/test_audit.py index 207a23c62c..0dd7b6a6b9 100644 --- a/tests/system/python/api/test_audit.py +++ b/tests/system/python/api/test_audit.py @@ -18,7 +18,7 @@ __license__ = "Apache 2.0" __version__ = "${VERSION}" -DEFAULT_AUDIT_COUNT = 16 +DEFAULT_AUDIT_COUNT = 17 class TestAudit: @@ -66,13 +66,13 @@ def test_get_severity(self, fledge_url): @pytest.mark.parametrize("request_params, total_count, audit_count", [ ('', DEFAULT_AUDIT_COUNT, DEFAULT_AUDIT_COUNT), ('?limit=1', DEFAULT_AUDIT_COUNT, 1), - ('?skip=4', DEFAULT_AUDIT_COUNT, 12), + ('?skip=4', DEFAULT_AUDIT_COUNT, 13), ('?limit=1&skip=8', DEFAULT_AUDIT_COUNT, 1), ('?source=START', 1, 1), - ('?source=CONAD', 15, 15), - ('?source=CONAD&limit=1', 15, 1), - ('?source=CONAD&skip=1', 15, 14), - ('?source=CONAD&skip=6&limit=1', 15, 1), + ('?source=CONAD', 16, 16), + ('?source=CONAD&limit=1', 16, 1), + ('?source=CONAD&skip=1', 16, 15), + ('?source=CONAD&skip=6&limit=1', 16, 1), ('?severity=INFORMATION', DEFAULT_AUDIT_COUNT, DEFAULT_AUDIT_COUNT), ('?severity=failure', 0, 0), ('?source=CONAD&severity=failure', 0, 0), diff --git a/tests/system/python/api/test_configuration.py b/tests/system/python/api/test_configuration.py index 90e08b8ca1..ae9333106f 100644 --- a/tests/system/python/api/test_configuration.py +++ b/tests/system/python/api/test_configuration.py @@ -127,6 +127,14 @@ def test_default(self, fledge_url, reset_and_start_fledge, wait_time, storage_pl "displayName": "Password Policy", "children": [ + ] + }, + { + "key": "firewall", + "description": "Allow and Denied list of IP addresses for Firewall", + "displayName": "Firewall", + "children": [ + ] } ] From af2ed42e968f0d07ebaca4e6b1a3c0c6e300836a Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 7 Aug 2024 13:07:51 +0530 Subject: [PATCH 45/91] textual feedback changes Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 4 +-- python/fledge/common/web/middleware.py | 18 +++++------ python/fledge/services/core/firewall.py | 32 ++++++++----------- python/fledge/services/core/server.py | 15 ++++----- tests/system/python/api/test_configuration.py | 2 +- 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index d3fdc1f677..8577fa4126 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -2083,8 +2083,8 @@ def _handle_config_items(self, cat_name: str, cat_value: dict) -> None: if 'cacheSize' in cat_value: self._cacheManager.max_cache_size = int(cat_value['cacheSize']['value']) elif cat_name == 'firewall': - from fledge.services.core import firewall - firewall.Firewall.IPList.save(data=cat_value) + from fledge.services.core.firewall import Firewall + Firewall.IPAddresses.save(data=cat_value) async def _check_updates_by_role(self, request: aiohttp.web_request.Request) -> str: async def get_role_name(): diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index 150609d233..5bb6c3b297 100644 --- a/python/fledge/common/web/middleware.py +++ b/python/fledge/common/web/middleware.py @@ -14,7 +14,7 @@ import jwt from fledge.common.logger import FLCoreLogger -from fledge.services.core import firewall +from fledge.services.core.firewall import Firewall from fledge.services.core.user_model import User @@ -48,7 +48,7 @@ async def middleware(request): _logger.debug("Received %s request for %s", request.method, request.path) request.is_auth_optional = True request.user = None - check_firewall_ip(request) + check_firewall(request) return await handler(request) return middleware @@ -73,7 +73,7 @@ async def middleware(request): _logger.debug("Received %s request for %s", request.method, request.path) request.is_auth_optional = False request.user = None - check_firewall_ip(request) + check_firewall(request) if request.method == 'OPTIONS': return await handler(request) @@ -234,17 +234,17 @@ async def validate_requests(request): raise web.HTTPForbidden -def check_firewall_ip(req: web.Request) -> None: +def check_firewall(req: web.Request) -> None: source_ip_address = req.transport.get_extra_info('peername')[0] if source_ip_address not in ['localhost', '127.0.0.1']: - firewall_ip_addresses = firewall.Firewall.IPList.get() - if 'allowList' in firewall_ip_addresses: - allowed = firewall_ip_addresses['allowList'] + firewall_ip_addresses = Firewall.IPAddresses.get() + if 'allowedIP' in firewall_ip_addresses: + allowed = firewall_ip_addresses['allowedIP'] if allowed: if source_ip_address not in allowed: raise web.HTTPForbidden else: - if 'denyList' in firewall_ip_addresses: - if source_ip_address in firewall_ip_addresses['denyList']: + if 'deniedIP' in firewall_ip_addresses: + if source_ip_address in firewall_ip_addresses['deniedIP']: raise web.HTTPForbidden diff --git a/python/fledge/services/core/firewall.py b/python/fledge/services/core/firewall.py index ada60fb89c..a88e6f2ac3 100644 --- a/python/fledge/services/core/firewall.py +++ b/python/fledge/services/core/firewall.py @@ -15,7 +15,7 @@ _logger = FLCoreLogger().get_logger(__name__) DEFAULT_CONFIG = { - 'allowList': { + 'allowedIP': { 'description': 'A list of allowed IP addresses', 'type': 'list', 'items': 'string', @@ -24,7 +24,7 @@ 'order': '1', 'permissions': ['admin'] }, - 'denyList': { + 'deniedIP': { 'description': 'A list of denied IP addresses', 'type': 'list', 'items': 'string', @@ -37,29 +37,25 @@ class Firewall: + """ Monitor and Control HTTP Network Traffic """ - __slots__ = ['cat_name', 'display_name', 'description', 'config'] - + __slots__ = ['category', 'display_name', 'description', 'config'] def __init__(self): - self.cat_name = 'firewall' + self.category = 'firewall' self.display_name = 'Firewall' - self.description = 'Allow and Denied list of IP addresses for Firewall' + self.description = 'Monitor and Control HTTP Network Traffic' self.config = DEFAULT_CONFIG - def __repr__(self): - template = ('Firewall info: ') + template = ('Firewall settings: ') return template.format(s=self) - def __str__(self): return self.__repr__() - - class IPList: - + class IPAddresses: @classmethod def get(cls) -> dict: @@ -67,16 +63,14 @@ def get(cls) -> dict: from fledge.services.core import server return server.Server._firewall_ip_addresses - @classmethod def save(cls, data: dict) -> None: # To avoid cyclic import from fledge.services.core import server - if 'allowList' in data: - server.Server._firewall_ip_addresses.update({'allowList': json.loads(data['allowList']['value'])}) - if 'denyList' in data: - server.Server._firewall_ip_addresses.update({'denyList': json.loads(data['denyList']['value'])}) - + if 'allowedIP' in data: + server.Server._firewall_ip_addresses.update({'allowedIP': json.loads(data['allowedIP']['value'])}) + if 'deniedIP' in data: + server.Server._firewall_ip_addresses.update({'deniedIP': json.loads(data['deniedIP']['value'])}) @classmethod def clear(cls) -> None: diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index 8f16c97b74..f5715bc988 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -50,7 +50,7 @@ from fledge.common.web.ssl_wrapper import SSLVerifier from fledge.services.core.api import exceptions as api_exception from fledge.services.core.api.control_service import acl_management as acl_management -from fledge.services.core import firewall +from fledge.services.core.firewall import Firewall __author__ = "Amarendra K. Sinha, Praveen Garg, Terris Linenbach, Massimiliano Pinto, Ashish Jabble" @@ -204,7 +204,6 @@ class Server: cert_file_name = '' """ cert file name """ - _REST_API_DEFAULT_CONFIG = { 'enableHttp': { 'description': 'Enable HTTP (disable to use HTTPS)', @@ -566,22 +565,20 @@ async def password_config(cls): _logger.exception(ex) raise - @classmethod async def firewall_config(cls): try: - obj = firewall.Firewall() - category = obj.cat_name - await cls._configuration_manager.create_category(category, obj.config, obj.description, True, - display_name=obj.display_name) + _firewall = Firewall() + category = _firewall.category + await cls._configuration_manager.create_category(category, _firewall.config, _firewall.description, True, + display_name=_firewall.display_name) config = await cls._configuration_manager.get_category_all_items(category) - firewall.Firewall.IPList.save(data=config) + Firewall.IPAddresses.save(data=config) await cls._configuration_manager.create_child_category("rest_api", [category]) except Exception as ex: _logger.exception(ex) raise - @classmethod async def service_config(cls): """ diff --git a/tests/system/python/api/test_configuration.py b/tests/system/python/api/test_configuration.py index ae9333106f..f8bc758dc7 100644 --- a/tests/system/python/api/test_configuration.py +++ b/tests/system/python/api/test_configuration.py @@ -131,7 +131,7 @@ def test_default(self, fledge_url, reset_and_start_fledge, wait_time, storage_pl }, { "key": "firewall", - "description": "Allow and Denied list of IP addresses for Firewall", + "description": "Monitor and Control HTTP Network Traffic", "displayName": "Firewall", "children": [ From 931ed4b56c72c3bf64f9b0a4f19bf2e5d7ecb0f5 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 7 Aug 2024 14:12:47 +0530 Subject: [PATCH 46/91] Removed server cyclic server imports and get replaced with Firewall shared singleton object Signed-off-by: ashish-jabble --- python/fledge/common/web/middleware.py | 5 ++ python/fledge/services/core/firewall.py | 85 ++++++++++++++----------- python/fledge/services/core/server.py | 3 - 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index 5bb6c3b297..b37b98ba7c 100644 --- a/python/fledge/common/web/middleware.py +++ b/python/fledge/common/web/middleware.py @@ -235,6 +235,11 @@ async def validate_requests(request): def check_firewall(req: web.Request) -> None: + # FIXME: Need to check on other environments like AWS, docker; May be ideal with socket + # import socket + # hostname = socket.gethostname() + # IPAddr = socket.gethostbyname(hostname) + source_ip_address = req.transport.get_extra_info('peername')[0] if source_ip_address not in ['localhost', '127.0.0.1']: firewall_ip_addresses = Firewall.IPAddresses.get() diff --git a/python/fledge/services/core/firewall.py b/python/fledge/services/core/firewall.py index a88e6f2ac3..9683c09a44 100644 --- a/python/fledge/services/core/firewall.py +++ b/python/fledge/services/core/firewall.py @@ -14,67 +14,80 @@ _logger = FLCoreLogger().get_logger(__name__) -DEFAULT_CONFIG = { - 'allowedIP': { - 'description': 'A list of allowed IP addresses', - 'type': 'list', - 'items': 'string', - 'default': "[]", - 'displayName': 'Allowed IP Addresses', - 'order': '1', - 'permissions': ['admin'] - }, - 'deniedIP': { - 'description': 'A list of denied IP addresses', - 'type': 'list', - 'items': 'string', - 'default': "[]", - 'displayName': 'Denied IP Addresses', - 'order': '2', - 'permissions': ['admin'] - } - } - - -class Firewall: + +class Singleton(object): + _shared_state = {} + + def __init__(self): + self.__dict__ = self._shared_state + + +class Firewall(Singleton): """ Monitor and Control HTTP Network Traffic """ - __slots__ = ['category', 'display_name', 'description', 'config'] + __slots__ = ['category', 'display_name', 'description', 'config', 'ip_addresses'] + + DEFAULT_CONFIG = { + 'allowedIP': { + 'description': 'A list of allowed IP addresses', + 'type': 'list', + 'items': 'string', + 'default': "[]", + 'displayName': 'Allowed IP Addresses', + 'order': '1', + 'permissions': ['admin'] + }, + 'deniedIP': { + 'description': 'A list of denied IP addresses', + 'type': 'list', + 'items': 'string', + 'default': "[]", + 'displayName': 'Denied IP Addresses', + 'order': '2', + 'permissions': ['admin'] + } + } def __init__(self): + super().__init__() + self.category = 'firewall' self.display_name = 'Firewall' self.description = 'Monitor and Control HTTP Network Traffic' - self.config = DEFAULT_CONFIG + self.config = self.DEFAULT_CONFIG + self.ip_addresses = {} def __repr__(self): template = ('Firewall settings: ') + 'description={s.description}, config={s.config}, ip_addresses={s.ip_addresses}>') return template.format(s=self) def __str__(self): return self.__repr__() + @classmethod + def get_instance(cls): + if not hasattr(cls, '_instance'): + cls._instance = cls() + return cls._instance + class IPAddresses: @classmethod def get(cls) -> dict: - # To avoid cyclic import - from fledge.services.core import server - return server.Server._firewall_ip_addresses + f = Firewall.get_instance() + return f.ip_addresses @classmethod def save(cls, data: dict) -> None: - # To avoid cyclic import - from fledge.services.core import server + f = Firewall.get_instance() if 'allowedIP' in data: - server.Server._firewall_ip_addresses.update({'allowedIP': json.loads(data['allowedIP']['value'])}) + f.ip_addresses.update({'allowedIP': json.loads(data['allowedIP']['value'])}) if 'deniedIP' in data: - server.Server._firewall_ip_addresses.update({'deniedIP': json.loads(data['deniedIP']['value'])}) + f.ip_addresses.update({'deniedIP': json.loads(data['deniedIP']['value'])}) @classmethod def clear(cls) -> None: - # To avoid cyclic import - from fledge.services.core import server - server.Server._firewall_ip_addresses = {} + f = Firewall.get_instance() + f.ip_addresses = {} diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index f5715bc988..8e51435118 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -362,9 +362,6 @@ class Server: _user_idle_session_timeout = 15 * 60 """ User idle session timeout (in minutes) """ - _firewall_ip_addresses = {} - """ IP addresses for firewall check """ - _INSTALLATION_DEFAULT_CONFIG = { 'maxUpdate': { 'description': 'Maximum updates per day', From 47b14e00806339ef9e20326f7c356692cecda833 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 8 Aug 2024 14:09:04 +0100 Subject: [PATCH 47/91] FOGL-8982 Add documentation on allow and deny lists (#1439) * FOGL-8982 Add documentation on allow and deny lists Signed-off-by: Mark Riddoch * Add missign file Signed-off-by: Mark Riddoch * Update after review comments Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- docs/images/firewall.jpg | Bin 0 -> 249892 bytes docs/securing_fledge.rst | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 docs/images/firewall.jpg diff --git a/docs/images/firewall.jpg b/docs/images/firewall.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3d25156c017a49c89d2ddd25547d6319502c1e3a GIT binary patch literal 249892 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

a*<~6o1eCK`NQ5hZn7)e4R<2=EX=NuYha4J@ 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

5YekLc_yro=OevX4fMs&ZuRu?A;LA& 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

6O*)^1+eC`|eU1jH*u|6pb1z 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 s_GfB*mh literal 0 HcmV?d00001 diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index 642e887acd..598ca4c070 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -18,6 +18,7 @@ .. |reset_password| image:: images/reset_password.jpg .. |certificate_store| image:: images/certificate_store.jpg .. |update_certificate| image:: images/update_certificate.jpg +.. |firewall| image:: images/firewall.jpg .. Links @@ -90,6 +91,25 @@ After enabling HTTPS and selecting save you must restart Fledge in order for the | |connection_https| | +--------------------+ +Allow & Deny Lists +================== + +Fledge supports a pair of optional lists of IP addresses that can be set to allow or deny access to the Fledge API. These lists can be accessed via the *Configuration* menu option in the user interface in the *General*, *Admin API*, *Firewall* configuration category. + ++------------+ +| |firewall| | ++------------+ + + - Clicking on the arrow icon beside each list will expand the list and show the current contents of the list. + + - Click on the *Add new item* link to create a new entry in the list. + + - To remove an entry from the list click on the *x* icon to the right of the list item. + +If the allow list is non-empty, then any access, including ping, to the Fledge API port will be checked to see if the source IP address of the request matches an entry in the allow list. If the address of the requester is not in this allow list then the API will not send any response to the caller and the connection will be closed. The only address that is exempt from this checking is the localhost via the loopback interface, 127.0.0.1. This is required for local management of the Fledge instance and must always be accessible. + +If the blocked list is non-empty then any access, including ping, to the API will check the source address of the caller to see if it is included in the block list. If it is then the connection will be closed without sending any response to the caller. Again the address 127.0.0.1 is immune from this test. + Requiring User Login ==================== From 0832dd4dec84329b94ece2c01eed318fcd50a066 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 13 Aug 2024 16:16:39 +0530 Subject: [PATCH 48/91] user model login exception unit test fixes Signed-off-by: ashish-jabble --- .../python/fledge/services/core/test_user_model.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/python/fledge/services/core/test_user_model.py b/tests/unit/python/fledge/services/core/test_user_model.py index 9bec4cb712..b462e497bf 100644 --- a/tests/unit/python/fledge/services/core/test_user_model.py +++ b/tests/unit/python/fledge/services/core/test_user_model.py @@ -657,17 +657,16 @@ async def test_login_exception(self): async def mock_get_category_item(): return {"value": "0"} - DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f" - curr_time = datetime.now().strftime(DATE_FORMAT) pwd_result = {'count': 1, 'rows': [{'role_id': '1', 'pwd': '3759bf3302f5481e8c9cc9472c6088ac', 'id': '1', 'pwd_last_changed': '2018-03-30 12:32:08.216159', - 'hash_algorithm': 'SHA256', 'block_until': curr_time, 'failed_attempts': 0}]} + 'hash_algorithm': 'SHA256', 'block_until': '', 'failed_attempts': 0}]} expected = {'message': 'Something went wrong', 'retryable': False, 'entryPoint': 'delete'} payload = {"return": ["pwd", "id", "role_id", "access_method", {"column": "pwd_last_changed", "format": "YYYY-MM-DD HH24:MI:SS.MS", - "alias": "pwd_last_changed"}, "real_name", "description", "hash_algorithm", "block_until", "failed_attempts"], "where": - {"column": "uname", "condition": "=", "value": "user", "and": {"column": "enabled", "condition": "=", - "value": "t"}}} + "alias": "pwd_last_changed"}, "real_name", "description", "hash_algorithm", + "block_until", "failed_attempts"], + "where": {"column": "uname", "condition": "=", "value": "user", + "and": {"column": "enabled", "condition": "=", "value": "t"}}} storage_client_mock = MagicMock(StorageClientAsync) # Changed in version 3.8: patch() now returns an AsyncMock if the target is an async function. @@ -686,8 +685,9 @@ async def mock_get_category_item(): with patch.object(User.Objects, 'check_password', return_value=True) as check_pwd_patch: with patch.object(storage_client_mock, 'insert_into_tbl', side_effect=StorageServerError(code=400, reason="blah", error=expected)): - with pytest.raises(ValueError) as excinfo: + with pytest.raises(Exception) as excinfo: await User.Objects.login('user', 'fledge', '0.0.0.0') + assert excinfo.type is ValueError assert str(excinfo.value) == expected['message'] check_pwd_patch.assert_called_once_with('3759bf3302f5481e8c9cc9472c6088ac', 'fledge', algorithm='SHA256') From af8050428f230d432bdb62b2ac054e34b1be345f Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 14 Aug 2024 12:33:16 +0100 Subject: [PATCH 49/91] FOGL-8961 Fix issue with shutdown when in prefetch loop of north service (#1441) * FOGL-8961 Fix issue with shutdown when in prefetch loop of north service Signed-off-by: Mark Riddoch * Resolve all cases and fix uninitialised variable Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- C/services/north/data_load.cpp | 19 +++++++++++++------ C/services/north/data_send.cpp | 12 +++++++++--- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index 15a6ce84ee..e63b61e5a2 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -129,9 +129,12 @@ void DataLoad::loadThread() while (!m_shutdown) { unsigned int block = waitForReadRequest(); - while (m_queue.size() < m_prefetchLimit) // Read another block if we have less than - // the prefetch limit already queued + while (m_shutdown == false && m_queue.size() < m_prefetchLimit) + { + // Read another block if we have less than + // the prefetch limit already queued readBlock(block); + } } } @@ -414,7 +417,7 @@ void DataLoad::bufferReadings(ReadingSet *readings) ReadingSet *DataLoad::fetchReadings(bool wait) { unique_lock lck(m_qMutex); - while (m_queue.empty()) + while (m_shutdown == false && m_queue.empty()) { if (m_perfMonitor && m_perfMonitor->isCollecting()) { @@ -430,9 +433,13 @@ ReadingSet *DataLoad::fetchReadings(bool wait) return NULL; } } - ReadingSet *rval = m_queue.front(); - m_queue.pop_front(); - if (m_queue.size() < m_prefetchLimit) // Read another block if we have less than 5 already queued + ReadingSet *rval = NULL; + if (!m_queue.empty()) + { + rval = m_queue.front(); + m_queue.pop_front(); + } + if (m_queue.size() < m_prefetchLimit && m_shutdown == false) // Read another block if we have less than 5 already queued { triggerRead(m_blockSize); } diff --git a/C/services/north/data_send.cpp b/C/services/north/data_send.cpp index 4c4fca4311..b3c2043d10 100755 --- a/C/services/north/data_send.cpp +++ b/C/services/north/data_send.cpp @@ -40,7 +40,7 @@ static void statsThread(DataSender *sender) * Constructor for the data sending class */ DataSender::DataSender(NorthPlugin *plugin, DataLoad *loader, NorthService *service) : - m_plugin(plugin), m_loader(loader), m_service(service), m_shutdown(false), m_paused(false), m_perfMonitor(NULL) + m_plugin(plugin), m_loader(loader), m_service(service), m_shutdown(false), m_paused(false), m_perfMonitor(NULL), m_sending(false) { m_statsUpdateFails = 0; @@ -104,7 +104,7 @@ void DataSender::sendThread() return; } bool removeReadings = false; - if (readings->getCount() > 0) + if (m_shutdown == false && readings->getCount() > 0) { unsigned long lastSent = send(readings); if (lastSent) @@ -117,7 +117,9 @@ void DataSender::sendThread() // Set readings removal removeReadings = vec->size() == 0; } - } else { + } + else if (m_shutdown == false) + { // All readings filtered out Logger::getLogger()->debug("All readings filtered out"); @@ -130,6 +132,10 @@ void DataSender::sendThread() // Set readings removal removeReadings = true; } + else + { + readings = NULL; + } // Remove readings object if needed if (removeReadings) From 924cf56247001a48b280c4a03328c0cdfa8d11ce Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 15 Aug 2024 09:15:05 +0100 Subject: [PATCH 50/91] FOGL-8591 Improve handling of exceptions in pipeline create and failure (#1444) to load south lugins. Also tidyup soem compiler warnings Signed-off-by: Mark Riddoch --- C/common/filter_pipeline.cpp | 30 ++++++++++++++++++-------- C/common/include/pipeline_element.h | 6 +++--- C/services/north/data_send.cpp | 8 +++---- C/services/north/include/data_sender.h | 4 ++-- C/services/south/south.cpp | 20 ++++++++++------- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/C/common/filter_pipeline.cpp b/C/common/filter_pipeline.cpp index ec09762737..bc59d69f45 100755 --- a/C/common/filter_pipeline.cpp +++ b/C/common/filter_pipeline.cpp @@ -248,10 +248,16 @@ bool FilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *useFil { (*it)->setNext(*(it + 1)); // Set next filter pointer as OUTPUT_HANDLE - if (!(*it)->init((OUTPUT_HANDLE *)(*(it + 1)), - filterReadingSetFn(passToOnwardFilter))) - { - errMsg += (*it)->getName() + "'"; + try { + if (!(*it)->init((OUTPUT_HANDLE *)(*(it + 1)), + filterReadingSetFn(passToOnwardFilter))) + { + errMsg += (*it)->getName() + "'"; + initErrors = true; + break; + } + } catch (exception& e) { + Logger::getLogger()->error("Unable to initialise plugin %s, %s", (*it)->getName().c_str(), e.what()); initErrors = true; break; } @@ -259,10 +265,16 @@ bool FilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *useFil else { // Set the Ingest class pointer as OUTPUT_HANDLE - if (!(*it)->init((OUTPUT_HANDLE *)(ingest), - filterReadingSetFn(useFilteredData))) - { - errMsg += (*it)->getName() + "'"; + try { + if (!(*it)->init((OUTPUT_HANDLE *)(ingest), + filterReadingSetFn(useFilteredData))) + { + errMsg += (*it)->getName() + "'"; + initErrors = true; + break; + } + } catch (exception& e) { + Logger::getLogger()->error("Unable to initialise plugin %s, %s", (*it)->getName().c_str(), e.what()); initErrors = true; break; } @@ -278,7 +290,7 @@ bool FilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *useFil if (initErrors) { // Failure - Logger::getLogger()->fatal("%s error: %s", __FUNCTION__, errMsg.c_str()); + Logger::getLogger()->fatal("Failed to create pipeline, %s", errMsg.c_str()); return false; } diff --git a/C/common/include/pipeline_element.h b/C/common/include/pipeline_element.h index 6f1c880218..415a453cd3 100644 --- a/C/common/include/pipeline_element.h +++ b/C/common/include/pipeline_element.h @@ -49,8 +49,8 @@ class PipelineElement { { ((PipelineElement *)handle)->ingest(readings); }; - virtual bool setupConfiguration(ManagementClient *mgtClient, - std::vector& children) + virtual bool setupConfiguration(ManagementClient * /* mgtClient */, + std::vector& /* children */) { return false; }; @@ -66,7 +66,7 @@ class PipelineElement { virtual bool setup(ManagementClient *mgmt, void *ingest, std::map& categories) = 0; virtual bool init(OUTPUT_HANDLE* outHandle, OUTPUT_STREAM output) = 0; virtual void shutdown(ServiceHandler *serviceHandler, ConfigHandler *configHandler) = 0; - virtual void reconfigure(const std::string& newConfig) + virtual void reconfigure(const std::string& /* newConfig */) { }; virtual std::string getName() = 0; diff --git a/C/services/north/data_send.cpp b/C/services/north/data_send.cpp index b3c2043d10..1db2bcbadc 100755 --- a/C/services/north/data_send.cpp +++ b/C/services/north/data_send.cpp @@ -312,7 +312,7 @@ void DataSender::flushStatistics() m_statsCv.wait_for(flush, std::chrono::seconds(FLUSH_STATS_INTERVAL)); flush.unlock(); - std::map statsData; + std::map statsData; // Acquire m_statsMtx lock for m_statsMtx unique_lock lck(m_statsMtx); @@ -335,7 +335,7 @@ void DataSender::flushStatistics() const Condition conditionStat(Equals); // Send statistics to storage service - map::iterator it; + map::iterator it; for (it = statsData.begin(); it != statsData.end(); it++) { // Prepare "WHERE key = name @@ -406,7 +406,7 @@ void DataSender::flushStatistics() * @return True for created data, False for no operation or error */ bool DataSender::createStats(const std::string &key, - int value) + unsigned int value) { if (!m_loader->getStorage()) { @@ -444,7 +444,7 @@ bool DataSender::createStats(const std::string &key, InsertValues values; values.push_back(InsertValue("key", key)); values.push_back(InsertValue("description", description)); - values.push_back(InsertValue("value", value)); + values.push_back(InsertValue("value", (long)value)); string table = "statistics"; if (m_loader->getStorage()->insertTable(table, values) != 1) diff --git a/C/services/north/include/data_sender.h b/C/services/north/include/data_sender.h index aea529f99d..55968c6af7 100644 --- a/C/services/north/include/data_sender.h +++ b/C/services/north/include/data_sender.h @@ -30,7 +30,7 @@ class DataSender { void flushStatistics(); private: void updateStatistics(uint32_t increment); - bool createStats(const std::string &key, int value); + bool createStats(const std::string &key, unsigned int value); unsigned long send(ReadingSet *readings); void blockPause(); void releasePause(); @@ -52,7 +52,7 @@ class DataSender { // Statistics save map std::condition_variable m_statsCv; std::mutex m_statsMtx; - std::map + std::map m_statsPendingEntries; int m_statsUpdateFails; // confirmed stats table entries diff --git a/C/services/south/south.cpp b/C/services/south/south.cpp index edf13c97c1..ee74632661 100644 --- a/C/services/south/south.cpp +++ b/C/services/south/south.cpp @@ -124,7 +124,7 @@ bool dryrun = false; } Logger *logger = Logger::getLogger(); logger->setMinLevel(logLevel); - // Start the service. This will oly return whren the serivce is shutdown + // Start the service. This will only return whren the serivce is shutdown service->start(coreAddress, corePort); delete service; delete logger; @@ -345,7 +345,9 @@ void SouthService::start(string& coreAddress, unsigned short corePort) m_config = m_mgtClient->getCategory(m_name); if (!loadPlugin()) { - logger->fatal("Failed to load south plugin, exiting..."); + logger->fatal("Failed to load south plugin %s, exiting...", m_name.c_str()); + string key = m_name + "LoadPlugin"; + m_mgtClient->raiseAlert(key, "South service " + m_name + " is shutting down due to a failure loading the south plugin"); management.stop(); return; } @@ -434,7 +436,7 @@ void SouthService::start(string& coreAddress, unsigned short corePort) m_throttle = false; } } - } catch (ConfigItemNotFound e) { + } catch (ConfigItemNotFound& e) { logger->info("Defaulting to inline defaults for south configuration"); } @@ -482,7 +484,7 @@ void SouthService::start(string& coreAddress, unsigned short corePort) logger->warn("Invalid setting of reading rate, defaulting to 1"); m_readingsPerSec = 1; } - } catch (ConfigItemNotFound e) { + } catch (ConfigItemNotFound& e) { logger->info("Defaulting to inline default for poll interval"); } @@ -490,8 +492,10 @@ void SouthService::start(string& coreAddress, unsigned short corePort) if (!ingest.loadFilters(m_name)) { string errMsg("'" + m_name + "' plugin: failed loading filter plugins."); - Logger::getLogger()->fatal((errMsg + " Exiting.").c_str()); - throw runtime_error(errMsg); + Logger::getLogger()->fatal((errMsg + " Shutting down south service.").c_str()); + string key = m_name + "LoadPipeline"; + m_mgtClient->raiseAlert(key, "South service " + m_name + " is shutting down due to a failure to create the data pipeline"); + return; } if (southPlugin->persistData()) @@ -849,7 +853,7 @@ bool SouthService::loadPlugin() return true; } - } catch (exception e) { + } catch (exception& e) { logger->fatal("Failed to load south plugin: %s\n", e.what()); } return false; @@ -993,7 +997,7 @@ void SouthService::processConfigChange(const string& categoryName, const string& { m_pollType = POLL_ON_DEMAND; } - } catch (ConfigItemNotFound e) { + } catch (ConfigItemNotFound& e) { logger->error("Failed to update poll interval following configuration change"); } } From f1f978caa197538184214af16c14de3e49de0e97 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 15 Aug 2024 09:47:35 +0100 Subject: [PATCH 51/91] FOGL-9021 Add backoff mechansim in north service if plugin_send fails (#1443) repeatedly Signed-off-by: Mark Riddoch --- C/services/north/data_send.cpp | 65 ++++++++++++++++++----- C/services/north/include/data_sender.h | 7 +++ C/services/north/include/north_service.h | 2 + C/services/north/north.cpp | 21 ++++++++ docs/images/NorthFailure.jpg | Bin 0 -> 14942 bytes docs/quick_start/north.rst | 12 +++++ 6 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 docs/images/NorthFailure.jpg diff --git a/C/services/north/data_send.cpp b/C/services/north/data_send.cpp index 1db2bcbadc..50a5e83d5c 100755 --- a/C/services/north/data_send.cpp +++ b/C/services/north/data_send.cpp @@ -40,7 +40,8 @@ static void statsThread(DataSender *sender) * Constructor for the data sending class */ DataSender::DataSender(NorthPlugin *plugin, DataLoad *loader, NorthService *service) : - m_plugin(plugin), m_loader(loader), m_service(service), m_shutdown(false), m_paused(false), m_perfMonitor(NULL), m_sending(false) + m_plugin(plugin), m_loader(loader), m_service(service), m_shutdown(false), m_paused(false), m_perfMonitor(NULL), m_sending(false), + m_repeatedFailure(0) { m_statsUpdateFails = 0; @@ -165,18 +166,56 @@ unsigned long DataSender::send(ReadingSet *readings) uint32_t sent = m_plugin->send(readings->getAllReadings()); releasePause(); - // last few readings in the reading set may have 0 reading ID, - // if they have been generated by filters on north service itself - const std::vector& readingsVec = readings->getAllReadings(); - unsigned long lastSent = 0; - for(auto rdngPtrItr = readingsVec.crbegin(); rdngPtrItr != readingsVec.crend(); rdngPtrItr++) - { - if((*rdngPtrItr)->hasId()) // only consider readings with valid reading IDs - { - lastSent = (*rdngPtrItr)->getId(); - break; - } - } + if (to_send > 0 && sent == 0) + { + m_repeatedFailure++; + // We had readings to send but sent known. This coudl be as a result + // of a failed connection north or a bad configuration, we have no way + // to tell. If we take no action we will continue to use lots of CPU + // and load the system. We instigate a backoff strategy here to try + // to keep some CPU available for other tasks + if (m_repeatedFailure == FAILURE_BACKOFF_THRESHOLD) + { + m_service->alertFailures(); + } + if (m_repeatedFailure > FAILURE_BACKOFF_THRESHOLD) + { + m_sendBackoffTime = m_repeatedFailure * MIN_SEND_BACKOFF / FAILURE_BACKOFF_THRESHOLD; + if (m_sendBackoffTime > MAX_SEND_BACKOFF) + { + m_sendBackoffTime = MAX_SEND_BACKOFF; + } + // Sleep in small periods to prevent blocking shutdown for too long + int cnt = m_sendBackoffTime / MIN_SEND_BACKOFF; + while (cnt-- > 0 && m_shutdown == false) + { + this_thread::sleep_for(chrono::milliseconds(MIN_SEND_BACKOFF)); + } + + } + } + else if (sent > 0) + { + if (m_repeatedFailure >= FAILURE_BACKOFF_THRESHOLD) + { + m_service->clearFailures(); + } + // Reset the backoff and continue at full rate + m_repeatedFailure = 0; + } + + // last few readings in the reading set may have 0 reading ID, + // if they have been generated by filters on north service itself + const std::vector& readingsVec = readings->getAllReadings(); + unsigned long lastSent = 0; + for (auto rdngPtrItr = readingsVec.crbegin(); rdngPtrItr != readingsVec.crend(); rdngPtrItr++) + { + if((*rdngPtrItr)->hasId()) // only consider readings with valid reading IDs + { + lastSent = (*rdngPtrItr)->getId(); + break; + } + } // unsigned long lastSent = readings->getReadingId(sent); if (m_perfMonitor) diff --git a/C/services/north/include/data_sender.h b/C/services/north/include/data_sender.h index 55968c6af7..5b70eb25d4 100644 --- a/C/services/north/include/data_sender.h +++ b/C/services/north/include/data_sender.h @@ -14,6 +14,11 @@ // Failure counter before re-recreating statics rows #define STATS_UPDATE_FAIL_THRESHOLD 3 +// BAckoff sending when we see repeated failures +#define FAILURE_BACKOFF_THRESHOLD 10 // Number of consequetive failures to trigger backoff +#define MIN_SEND_BACKOFF 50 // Min backoff in milliseconds +#define MAX_SEND_BACKOFF 500 // Max backoff in milliseconds + class DataLoad; class NorthService; @@ -58,5 +63,7 @@ class DataSender { // confirmed stats table entries std::unordered_set m_statsDbEntriesCache; + unsigned int m_repeatedFailure; + unsigned int m_sendBackoffTime; }; #endif diff --git a/C/services/north/include/north_service.h b/C/services/north/include/north_service.h index 03c12ff936..ac25c3edea 100644 --- a/C/services/north/include/north_service.h +++ b/C/services/north/include/north_service.h @@ -54,6 +54,8 @@ class NorthService : public ServiceAuthHandler { int operation(const std::string& name, int paramCount, char *names[], char *parameters[], const ControlDestination); int operation(const std::string& name, int paramCount, char *names[], char *parameters[], const ControlDestination, const std::string& arg); void setDryRun() { m_dryRun = true; }; + void alertFailures(); + void clearFailures(); private: void addConfigDefaults(DefaultConfigCategory& defaults); bool loadPlugin(); diff --git a/C/services/north/north.cpp b/C/services/north/north.cpp index 0913054624..8e4752d3b6 100755 --- a/C/services/north/north.cpp +++ b/C/services/north/north.cpp @@ -1316,3 +1316,24 @@ string NorthService::controlSource() return source; } + +/** + * Raise an alert that we are having issues sending data + */ +void NorthService::alertFailures() +{ + string key = "North " + m_name; + string message = "Repeated failures to send data via the " + m_name + " north service "; + m_mgtClient->raiseAlert(key, message, "normal"); + logger->warn("Repeated failures to send data to destination"); +} + +/** + * Clear the failure alert for sending data + * + * Currently we hae no way to clear the actual alert, so we simply log the fact + */ +void NorthService::clearFailures() +{ + logger->warn("The sending of data has resumed"); +} diff --git a/docs/images/NorthFailure.jpg b/docs/images/NorthFailure.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2fcfa5632bfb245127bbb9800960d321a4efd8d9 GIT binary patch literal 14942 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 Date: Fri, 16 Aug 2024 11:06:13 +0530 Subject: [PATCH 52/91] Python metadata fixes in setup file Signed-off-by: ashish-jabble --- python/setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/setup.py b/python/setup.py index fcf71f7fda..ad7351db46 100644 --- a/python/setup.py +++ b/python/setup.py @@ -2,11 +2,11 @@ setup( name='Fledge', - python_requires='~=3.5', - version='0.1', - description='Fledge', - url='http://github.com/fledge/Fledge', - author='OSIsoft, LLC', + python_requires='>=3.6.9', + version='2.5.0', + description='Fledge, the open source platform for the Internet of Things', + url='https://github.com/fledge-iot/fledge', + author='Dianomic Systems Inc.', author_email='info@dianomic.com', license='Apache 2.0', # TODO: list of excludes (tests) From e28ca99ed554c9a59899385c830d1af43ccd666a Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Fri, 16 Aug 2024 09:21:35 +0100 Subject: [PATCH 53/91] FOGL-9029 Clear alert for failure to send data north and update documentation (#1445) * Initial checkin Signed-off-by: Mark Riddoch * FOGL-9029 Clar north send failure alert, update documentation Signed-off-by: Mark Riddoch * Fixes for review comments Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- C/common/include/management_client.h | 1 + C/common/management_client.cpp | 27 +++++++++++++++++++ C/services/north/north.cpp | 9 +++++-- docs/quick_start/north.rst | 22 +++++++++++++-- .../common/microservice_management/routes.py | 1 + python/fledge/services/core/server.py | 15 +++++++++++ 6 files changed, 71 insertions(+), 4 deletions(-) diff --git a/C/common/include/management_client.h b/C/common/include/management_client.h index 97f16e7c53..993db6ac11 100644 --- a/C/common/include/management_client.h +++ b/C/common/include/management_client.h @@ -119,6 +119,7 @@ class ManagementClient { AssetTrackingTable *getDeprecatedAssetTrackingTuples(); std::string getAlertByKey(const std::string& key); bool raiseAlert(const std::string& key, const std::string& message, const std::string& urgency="normal"); + bool clearAlert(const std::string& key); private: std::ostringstream m_urlbase; diff --git a/C/common/management_client.cpp b/C/common/management_client.cpp index 9057c90da2..badef34589 100644 --- a/C/common/management_client.cpp +++ b/C/common/management_client.cpp @@ -2136,3 +2136,30 @@ bool ManagementClient::raiseAlert(const std::string& key, const std::string& mes } } +/** + * Clear an alert + * + * @param key Alert key + * @return whether operation was successful + */ +bool ManagementClient::clearAlert(const std::string& key) +{ + try + { + std::string url = "/fledge/alert/" + urlEncode(key); + auto res = this->getHttpClient()->request("DELETE", url.c_str()); + std::string statusCode = res->status_code; + if (statusCode.compare("200 OK")) + { + m_logger->error("Clear alert failed %s.", statusCode.c_str()); + return false; + } + + return true; + } + catch (const SimpleWeb::system_error &e) { + m_logger->error("Clear alert failed %s.", e.what()); + return false; + } +} + diff --git a/C/services/north/north.cpp b/C/services/north/north.cpp index 8e4752d3b6..5c07207f5b 100755 --- a/C/services/north/north.cpp +++ b/C/services/north/north.cpp @@ -1319,6 +1319,8 @@ string NorthService::controlSource() /** * Raise an alert that we are having issues sending data + * + * We also write a warning to the system log to aid with debugging */ void NorthService::alertFailures() { @@ -1331,9 +1333,12 @@ void NorthService::alertFailures() /** * Clear the failure alert for sending data * - * Currently we hae no way to clear the actual alert, so we simply log the fact + * We clear the alert from the status bar and write a message to the system + * log */ void NorthService::clearFailures() { - logger->warn("The sending of data has resumed"); + string key = "North " + m_name; + m_mgtClient->clearAlert(key); + logger->info("The sending of data has resumed"); } diff --git a/docs/quick_start/north.rst b/docs/quick_start/north.rst index b4e998a5a4..a43dda1807 100644 --- a/docs/quick_start/north.rst +++ b/docs/quick_start/north.rst @@ -19,9 +19,17 @@ Adding Data Destinations To add a data destination, click on “Create North Instance+” in the upper right of the North Services screen. Fledge will display a series of 3 screens to add the data destination: 1. The first screen will ask you to select the plugin for the data destination from the list of installed plugins. If you do not see the plugin you need, refer to the Installing Fledge section of this manual. In addition, this screen allows you to specify a display name for the data destination. In addition, you can specify how frequently data will be forwarded to the destination in days, hours, minutes and seconds. Enter the number of days in the interval in the left box and the number of hours, minutes and seconds in format HH:MM:SS in the right box. -2. The second screen allows you to configure the plugin and the data assets it will send. See the section below for specifics of configuring a PI, EDS or OCS destination. +2. The second screen allows you to configure the plugin and the data assets it will send. See the section below for specifics of configuring a PI, EDS, OCS or ADH destination. + .. note:: + + An option exists to run a service rather than a task in the north. If run as a service there is no schedule and data is sent as soon as it is available. It is recommended, if you have no connection restrictions, to run the north as a service rather than a task as this will give the best performance. + 3. The final screen loads the plugin. You can specify whether it will be enabled immediately for data sending or to await enabling in the future. +.. note:: + + Fledge supports multiple plugins to send to different north destinations. Multiple north tasks and/or services may be created to send data simultaneously to multiple destinations. + Configuring Data Destinations ############################# @@ -43,8 +51,18 @@ If Fledge is unable to send data to another system via a north service it will w The failure could be an incorrect configuration within Fledge for the particular north plugin or it may be caused by the upstream system being unavailable or there being no network route to the upstream system. +Once the failure is cleared, the alert will be removed from the status bar. + +.. note:: + + If Fledge is shutdown and when it is later restarted the problem has cleared, the alert may persist in the status bar and should be manually cleared. If the alert is manually cleared but the problem persists then the alert will be recreated. + Using the OMF plugin #################### -OSISoft data historians are one of the most common destinations for Fledge data. Fledge supports the full range of OSISoft historians; the PI System, Edge Data Store (EDS) and OSISoft Cloud Services (OCS). To send data to a PI server you may use either the older PI Connector Relay or the newer PI Web API OMF endpoint. It is recommended that new users use the PI Web API OMF endpoint rather then the Connector Relay which is no longer supported by OSIsoft. +AVEVA PI (formerly OSISoft PI) data historians are one of the most common destinations for Fledge data. Fledge supports the full range of AVEVA historians: the PI System, Edge Data Store (EDS), OSIsoft Cloud Services (OCS) and AVEVA Data Hub (ADH). To send data to a PI Server you may use either the older PI Connector Relay or the newer PI Web API OMF endpoint. It is recommended that new users use the PI Web API OMF endpoint rather than the Connector Relay which is no longer supported by AVEVA. + +.. note:: + + The AVEVA Data Hub is now known as CONNECT diff --git a/python/fledge/services/common/microservice_management/routes.py b/python/fledge/services/common/microservice_management/routes.py index 6a27848b7c..ff45a0bea6 100644 --- a/python/fledge/services/common/microservice_management/routes.py +++ b/python/fledge/services/common/microservice_management/routes.py @@ -76,6 +76,7 @@ def setup(app, obj, is_core=False): # alerts app.router.add_route('GET', '/fledge/alert/{key}', obj.get_alert) app.router.add_route('POST', '/fledge/alert', obj.add_alert) + app.router.add_route('DELETE', '/fledge/alert/{key}', obj.delete_alert) # Proxy API setup for a microservice proxy.setup(app) diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index 8e51435118..6e7501301a 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -2135,6 +2135,21 @@ async def get_alert(cls, request): else: return web.json_response({"alert": alert}) + @classmethod + async def delete_alert(cls, request): + name = request.match_info.get('key', None) + try: + alert = await cls._alert_manager.delete(name) + except KeyError as err: + msg = str(err.args[0]) + return web.HTTPNotFound(reason=msg, body=json.dumps({"message": msg})) + except Exception as ex: + msg = str(ex) + _logger.error(ex, "Failed to delete an alert.") + raise web.HTTPInternalServerError(reason=msg, body=json.dumps({"message": msg})) + else: + return web.json_response({"alert": alert}) + @classmethod async def add_alert(cls, request): try: From cf0cbdb47752cb0a9465586c39b8bb07ab39f4ee Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 16 Aug 2024 14:42:22 +0530 Subject: [PATCH 54/91] DOC fixes for FOGL-9029 changes Signed-off-by: ashish-jabble --- docs/quick_start/north.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/quick_start/north.rst b/docs/quick_start/north.rst index a43dda1807..49b9763c5d 100644 --- a/docs/quick_start/north.rst +++ b/docs/quick_start/north.rst @@ -16,15 +16,16 @@ The North Services screen displays the status of all data sending processes in t Adding Data Destinations ######################## -To add a data destination, click on “Create North Instance+” in the upper right of the North Services screen. Fledge will display a series of 3 screens to add the data destination: +To add a data destination, click on “Create North Instance+” in the upper right of the North Services screen. Fledge will display a series of 3 screens to add the data destination: + +1. The first screen will ask you to select the plugin for the data destination from the list of installed plugins. If you do not see the plugin you need, refer to the Installing Fledge section of this manual. In addition, this screen allows you to specify a display name for the data destination. In addition, you can specify how frequently data will be forwarded to the destination in days, hours, minutes and seconds. Enter the number of days in the interval in the left box and the number of hours, minutes and seconds in format HH:MM:SS in the right box. +2. The second screen allows you to configure the plugin and the data assets it will send. See the section below for specifics of configuring a PI, EDS, OCS or ADH destination. -1. The first screen will ask you to select the plugin for the data destination from the list of installed plugins. If you do not see the plugin you need, refer to the Installing Fledge section of this manual. In addition, this screen allows you to specify a display name for the data destination. In addition, you can specify how frequently data will be forwarded to the destination in days, hours, minutes and seconds. Enter the number of days in the interval in the left box and the number of hours, minutes and seconds in format HH:MM:SS in the right box. -2. The second screen allows you to configure the plugin and the data assets it will send. See the section below for specifics of configuring a PI, EDS, OCS or ADH destination. .. note:: An option exists to run a service rather than a task in the north. If run as a service there is no schedule and data is sent as soon as it is available. It is recommended, if you have no connection restrictions, to run the north as a service rather than a task as this will give the best performance. -3. The final screen loads the plugin. You can specify whether it will be enabled immediately for data sending or to await enabling in the future. +3. The final screen loads the plugin. You can specify whether it will be enabled immediately for data sending or to await enabling in the future. .. note:: From f444ccb908c37d0ba9f409beae5444ba856536a5 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Fri, 16 Aug 2024 12:17:43 +0100 Subject: [PATCH 55/91] FOGL-9027 Resolve issue with Python filters sending data during (#1446) * FOGL-9027 Resolve issue with Python fitlers sending data during shutdown. Signed-off-by: Mark Riddoch * Update for review comments Signed-off-by: Mark Riddoch * Update typos Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- C/common/filter_pipeline.cpp | 20 +++++++++++++++++--- C/services/south/ingest.cpp | 9 +++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/C/common/filter_pipeline.cpp b/C/common/filter_pipeline.cpp index bc59d69f45..738dc118ba 100755 --- a/C/common/filter_pipeline.cpp +++ b/C/common/filter_pipeline.cpp @@ -311,13 +311,27 @@ bool FilterPipeline::setupFiltersPipeline(void *passToOnwardFilter, void *useFil */ void FilterPipeline::cleanupFilters(const string& categoryName) { - // Cleanup filters, in reverse order - for (auto it = m_filters.rbegin(); it != m_filters.rend(); ++it) + + // Shutdown filters - do this down the pipeline, starting + // from the first filter in the pipeline. This allows a filter + // to asynchronously send data in the shutdown call to the + // next element in the pipeline since that next element has + // not yet been asked to shutdown. + // + // This is not behaviour that is encouraged or designed, but a + // small number of Python filters have implemented sending data + // during shutdown, hence the need to ensure that data has + // somewhere to go. + for (auto it = m_filters.begin(); it != m_filters.end(); ++it) { PipelineElement *element = *it; ConfigHandler *configHandler = ConfigHandler::getInstance(mgtClient); element->shutdown(m_serviceHandler, configHandler); - + } + // Delete filters, in reverse order + for (auto it = m_filters.rbegin(); it != m_filters.rend(); ++it) + { + PipelineElement *element = *it; // Free filter delete element; } diff --git a/C/services/south/ingest.cpp b/C/services/south/ingest.cpp index 45d8706de3..dec1969391 100755 --- a/C/services/south/ingest.cpp +++ b/C/services/south/ingest.cpp @@ -1056,6 +1056,15 @@ void Ingest::useFilteredData(OUTPUT_HANDLE *outHandle, lock_guard guard(ingest->m_useDataMutex); vector *newData = readingSet->getAllReadingsPtr(); + if (!ingest->m_data) + { + // If we are called during shutdown there will be no m_data in place + // and we create a new one to handle this special case. In this case + // the m_data will not be explicitly deleted. However as we are shutting + // down this will note cause a problem as all memory is recovered at process + // exit time. + ingest->m_data = new vector; + } ingest->m_data->insert(ingest->m_data->end(), newData->cbegin(), newData->cend()); readingSet->clear(); From b2992fd517cf0a68b7bcc0a430b9dabd531de1fc Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 16 Aug 2024 21:04:22 +0530 Subject: [PATCH 56/91] original author reverted Signed-off-by: ashish-jabble --- python/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/setup.py b/python/setup.py index ad7351db46..9738d3b4e5 100644 --- a/python/setup.py +++ b/python/setup.py @@ -6,7 +6,7 @@ version='2.5.0', description='Fledge, the open source platform for the Internet of Things', url='https://github.com/fledge-iot/fledge', - author='Dianomic Systems Inc.', + author='OSIsoft, LLC; Dianomic Systems Inc.', author_email='info@dianomic.com', license='Apache 2.0', # TODO: list of excludes (tests) From 3215df3b3b67bc190fd86d5f63964abd9ac512ac Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 21 Aug 2024 12:46:13 +0530 Subject: [PATCH 57/91] datetime format helper function added in tests Signed-off-by: ashish-jabble --- tests/system/python/helpers/utils.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/system/python/helpers/utils.py b/tests/system/python/helpers/utils.py index e17869d925..1d602dd447 100644 --- a/tests/system/python/helpers/utils.py +++ b/tests/system/python/helpers/utils.py @@ -4,6 +4,8 @@ # See: http://fledge-iot.readthedocs.io/ # FLEDGE_END +from datetime import datetime + import http.client import json @@ -64,4 +66,27 @@ def delete_request(fledge_url, delete_url): res = con.getresponse() assert 200 == res.status, "ERROR! GET {} request failed".format(delete_url) r = json.loads(res.read().decode()) - return r \ No newline at end of file + return r + + +def check_datetime_format(datetime_str, format_str=None): + """ + Check if the given datetime string matches the specified format. + + Parameters: + - datetime_str: The datetime string to check. + - format_str: The format string that the datetime string should match. + + Returns: + - True if the string matches the format, False otherwise. + """ + try: + # Attempt to parse the datetime string with the specified format + if format_str is None: + format_str = "%Y-%m-%d %H:%M:%S.%f" + datetime.strptime(datetime_str, format_str) + return True + except ValueError: + # Parsing failed; the string does not match the format + return False + From 0e3bcad5a20008af415ae325099d65fd7f4002f0 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 21 Aug 2024 12:46:50 +0530 Subject: [PATCH 58/91] timestamp format verification added in existing API tests Signed-off-by: ashish-jabble --- tests/system/python/api/test_alerts.py | 3 +++ tests/system/python/api/test_audit.py | 7 +++++++ tests/system/python/api/test_statistics.py | 10 +++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/system/python/api/test_alerts.py b/tests/system/python/api/test_alerts.py index ef0b19c4cc..6d63ae95ad 100644 --- a/tests/system/python/api/test_alerts.py +++ b/tests/system/python/api/test_alerts.py @@ -109,6 +109,9 @@ def test_get_all_alerts(self, fledge_url): assert 'urgency' in alert_jdoc assert 'Normal' == alert_jdoc['urgency'] assert 'timestamp' in alert_jdoc + import utils + is_matching = utils.check_datetime_format(alert_jdoc['timestamp']) + assert is_matching == True, "Timestamp format mismatched." def test_delete_alert_by_key(self, fledge_url): payload = {"key": "Sine", "message": "The service has restarted 4 times", "urgency": "critical"} diff --git a/tests/system/python/api/test_audit.py b/tests/system/python/api/test_audit.py index 0dd7b6a6b9..9815cecc5d 100644 --- a/tests/system/python/api/test_audit.py +++ b/tests/system/python/api/test_audit.py @@ -12,6 +12,7 @@ import time from collections import Counter import pytest +import utils __author__ = "Ashish Jabble" __copyright__ = "Copyright (c) 2019 Dianomic Systems" @@ -95,6 +96,10 @@ def test_default_get_audit(self, fledge_url, wait_time, request_params, total_co assert len(jdoc), "No data found" assert total_count == jdoc['totalCount'] assert audit_count == len(elems) + if len(elems): + is_matching = utils.check_datetime_format(elems[0]['timestamp']) + assert is_matching == True, "Timestamp format mismatched." + @pytest.mark.parametrize("payload, total_count", [ ({"source": "LOGGN", "severity": "warning", "details": {"message": "Engine oil pressure low"}}, 1), @@ -113,6 +118,8 @@ def test_create_audit_entry(self, fledge_url, payload, total_count): assert payload['source'] == jdoc['source'] assert payload['severity'] == jdoc['severity'] assert payload['details'] == jdoc['details'] + is_matching = utils.check_datetime_format(jdoc['timestamp']) + assert is_matching == True, "Timestamp format mismatched." # Verify new audit log entries conn.request("GET", '/fledge/audit') diff --git a/tests/system/python/api/test_statistics.py b/tests/system/python/api/test_statistics.py index 17baf5a6c9..76aa7fbe31 100644 --- a/tests/system/python/api/test_statistics.py +++ b/tests/system/python/api/test_statistics.py @@ -99,6 +99,8 @@ def test_statistics_history_with_stats_collector_schedule(self, fledge_url, wait statistics = jdoc['statistics'][0] if i <= retries and Counter(STATS_HISTORY_KEYS) != Counter(statistics.keys()): continue assert Counter(STATS_HISTORY_KEYS) == Counter(statistics.keys()) + is_matching = utils.check_datetime_format(statistics['history_ts']) + assert is_matching == True, "History Timestamp format mismatched." del statistics['history_ts'] assert Counter([0, 0, 0, 0, 0, 0]) == Counter(statistics.values()) break @@ -121,6 +123,9 @@ def test_statistics_history_with_params(self, fledge_url, request_params, keys): assert 15 == jdoc['interval'] assert 1 == len(jdoc['statistics']) assert Counter(keys) == Counter(jdoc['statistics'][0].keys()) + if keys: + is_matching = utils.check_datetime_format(jdoc['statistics'][0]['history_ts']) + assert is_matching == True, "History Timestamp format mismatched." def test_statistics_history_with_service_enabled(self, start_south_coap, fledge_url, wait_time): # Allow CoAP listener to start @@ -160,7 +165,6 @@ def test_statistics_history_with_service_enabled(self, start_south_coap, fledge_ read = [r['READINGS'] for r in stats_history] assert 1 in read assert 1 == read.count(1) - # print(stats_history) asset_stats_history = [a for a in stats_history if ASSET_NAME.upper() in a.keys()] assert any(ash[ASSET_NAME.upper()] == 1 for ash in asset_stats_history), "Failed to find statistics history " \ "record for " + ASSET_NAME.upper() @@ -172,6 +176,10 @@ def test_statistics_history_with_service_enabled(self, start_south_coap, fledge_ r = r.read().decode() jdoc = json.loads(r) assert len(jdoc), "No data found" + for stat in jdoc['statistics']: + is_matching = utils.check_datetime_format(stat['history_ts']) + assert is_matching == True, "History Timestamp format mismatched." + read = [r['READINGS'] for r in jdoc['statistics']] assert 1 in read assert 1 == read.count(1) From e2ccef4e0563136e3e34a0c66c781c4647832e11 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 21 Aug 2024 13:19:56 +0530 Subject: [PATCH 59/91] browser API tests date format verification with helper function and other code refactoring Signed-off-by: ashish-jabble --- tests/system/python/api/test_alerts.py | 4 +-- tests/system/python/api/test_audit.py | 7 ++--- .../system/python/api/test_browser_assets.py | 26 +++++++------------ tests/system/python/api/test_statistics.py | 11 ++++---- tests/system/python/helpers/utils.py | 8 +++--- 5 files changed, 22 insertions(+), 34 deletions(-) diff --git a/tests/system/python/api/test_alerts.py b/tests/system/python/api/test_alerts.py index 6d63ae95ad..a252fca99e 100644 --- a/tests/system/python/api/test_alerts.py +++ b/tests/system/python/api/test_alerts.py @@ -40,6 +40,7 @@ def create_alert(url, payload): assert len(jdoc), "Failed to create alert!" return jdoc + class TestAlerts: def test_get_default_alerts(self, fledge_url, reset_and_start_fledge): @@ -110,8 +111,7 @@ def test_get_all_alerts(self, fledge_url): assert 'Normal' == alert_jdoc['urgency'] assert 'timestamp' in alert_jdoc import utils - is_matching = utils.check_datetime_format(alert_jdoc['timestamp']) - assert is_matching == True, "Timestamp format mismatched." + assert utils.validate_date_format(alert_jdoc['timestamp']) is True, "Timestamp format mismatched." def test_delete_alert_by_key(self, fledge_url): payload = {"key": "Sine", "message": "The service has restarted 4 times", "urgency": "critical"} diff --git a/tests/system/python/api/test_audit.py b/tests/system/python/api/test_audit.py index 9815cecc5d..4007204a10 100644 --- a/tests/system/python/api/test_audit.py +++ b/tests/system/python/api/test_audit.py @@ -97,9 +97,7 @@ def test_default_get_audit(self, fledge_url, wait_time, request_params, total_co assert total_count == jdoc['totalCount'] assert audit_count == len(elems) if len(elems): - is_matching = utils.check_datetime_format(elems[0]['timestamp']) - assert is_matching == True, "Timestamp format mismatched." - + assert utils.validate_date_format(elems[0]['timestamp']) is True, "Timestamp format mismatched." @pytest.mark.parametrize("payload, total_count", [ ({"source": "LOGGN", "severity": "warning", "details": {"message": "Engine oil pressure low"}}, 1), @@ -118,8 +116,7 @@ def test_create_audit_entry(self, fledge_url, payload, total_count): assert payload['source'] == jdoc['source'] assert payload['severity'] == jdoc['severity'] assert payload['details'] == jdoc['details'] - is_matching = utils.check_datetime_format(jdoc['timestamp']) - assert is_matching == True, "Timestamp format mismatched." + assert utils.validate_date_format(jdoc['timestamp']) is True, "Timestamp format mismatched." # Verify new audit log entries conn.request("GET", '/fledge/audit') diff --git a/tests/system/python/api/test_browser_assets.py b/tests/system/python/api/test_browser_assets.py index 8c6032b95a..68bf6e15c2 100644 --- a/tests/system/python/api/test_browser_assets.py +++ b/tests/system/python/api/test_browser_assets.py @@ -13,8 +13,7 @@ import time import json import pytest -from datetime import datetime - +import utils __author__ = "Ashish Jabble, Vaibhav Singhal" __copyright__ = "Copyright (c) 2019 Dianomic Systems" @@ -28,15 +27,6 @@ SERVICE_NAME = 'TestBrowserAPI' -def validate_date_format(dt_txt, fmt): - try: - datetime.strptime(dt_txt, fmt) - except ValueError: - return False - else: - return True - - class TestBrowserAssets: @pytest.fixture @@ -278,7 +268,8 @@ def test_get_asset_series_query_group_sec(self, fledge_url): assert SENSOR_VALUES[i] == jdoc[i]['min'] assert SENSOR_VALUES[i] == jdoc[i]['average'] assert SENSOR_VALUES[i] == jdoc[i]['max'] - assert validate_date_format(jdoc[i]['timestamp'], '%Y-%m-%d %H:%M:%S'), "timestamp format do not match" + assert utils.validate_date_format(jdoc[i]['timestamp'], '%Y-%m-%d %H:%M:%S') is True, \ + "Timestamp format mismatched." def test_get_asset_series_query_group_min(self, fledge_url, wait_time): """Test that browsing an asset's data point time series with minutes grouping @@ -295,13 +286,15 @@ def test_get_asset_series_query_group_min(self, fledge_url, wait_time): assert (sum(SENSOR_VALUES[0:2]) / len(SENSOR_VALUES[0:2])) == jdoc[0]['average'] assert min(SENSOR_VALUES[0:2]) == jdoc[0]['min'] assert max(SENSOR_VALUES[0:2]) == jdoc[0]['max'] - assert validate_date_format(jdoc[0]['timestamp'], '%Y-%m-%d %H:%M'), "timestamp format do not match" + assert utils.validate_date_format(jdoc[0]['timestamp'], '%Y-%m-%d %H:%M') is True, \ + "Timestamp format mismatched." for i in range(1, len(jdoc) - 1): assert SENSOR_VALUES[i + 1] == jdoc[i]['min'] assert SENSOR_VALUES[i + 1] == jdoc[i]['average'] assert SENSOR_VALUES[i + 1] == jdoc[i]['max'] - assert validate_date_format(jdoc[i + 1]['timestamp'], '%Y-%m-%d %H:%M'), "timestamp format do not match" + assert utils.validate_date_format(jdoc[i + 1]['timestamp'], '%Y-%m-%d %H:%M') is True, \ + "Timestamp format mismatched." def test_get_asset_series_query_group_hrs(self, fledge_url, wait_time): """Test that browsing an asset's data point time series with hour grouping @@ -318,13 +311,14 @@ def test_get_asset_series_query_group_hrs(self, fledge_url, wait_time): assert (sum(SENSOR_VALUES[0:4]) / len(SENSOR_VALUES[0:4])) == jdoc[0]['average'] assert min(SENSOR_VALUES[0:4]) == jdoc[0]['min'] assert max(SENSOR_VALUES[0:4]) == jdoc[0]['max'] - assert validate_date_format(jdoc[0]['timestamp'], '%Y-%m-%d %H'), "timestamp format do not match" + assert utils.validate_date_format(jdoc[0]['timestamp'], '%Y-%m-%d %H') is True, "Timestamp format mismatched." for i in range(4, 6): assert SENSOR_VALUES[i] == jdoc[i - 3]['min'] assert SENSOR_VALUES[i] == jdoc[i - 3]['average'] assert SENSOR_VALUES[i] == jdoc[i - 3]['max'] - assert validate_date_format(jdoc[i - 3]['timestamp'], '%Y-%m-%d %H'), "timestamp format do not match" + assert utils.validate_date_format(jdoc[i - 3]['timestamp'], '%Y-%m-%d %H') is True, \ + "Timestamp format mismatched." def test_get_asset_sensor_readings_invalid_group(self, fledge_url): """Test that browsing an asset's data point time series with invalid grouping diff --git a/tests/system/python/api/test_statistics.py b/tests/system/python/api/test_statistics.py index 76aa7fbe31..d8a9b73a0f 100644 --- a/tests/system/python/api/test_statistics.py +++ b/tests/system/python/api/test_statistics.py @@ -99,8 +99,8 @@ def test_statistics_history_with_stats_collector_schedule(self, fledge_url, wait statistics = jdoc['statistics'][0] if i <= retries and Counter(STATS_HISTORY_KEYS) != Counter(statistics.keys()): continue assert Counter(STATS_HISTORY_KEYS) == Counter(statistics.keys()) - is_matching = utils.check_datetime_format(statistics['history_ts']) - assert is_matching == True, "History Timestamp format mismatched." + assert utils.validate_date_format(statistics['history_ts']) is True, "History Timestamp format mismatched." + del statistics['history_ts'] assert Counter([0, 0, 0, 0, 0, 0]) == Counter(statistics.values()) break @@ -124,8 +124,8 @@ def test_statistics_history_with_params(self, fledge_url, request_params, keys): assert 1 == len(jdoc['statistics']) assert Counter(keys) == Counter(jdoc['statistics'][0].keys()) if keys: - is_matching = utils.check_datetime_format(jdoc['statistics'][0]['history_ts']) - assert is_matching == True, "History Timestamp format mismatched." + assert utils.validate_date_format(jdoc['statistics'][0]['history_ts']) is True, \ + "History Timestamp format mismatched." def test_statistics_history_with_service_enabled(self, start_south_coap, fledge_url, wait_time): # Allow CoAP listener to start @@ -177,8 +177,7 @@ def test_statistics_history_with_service_enabled(self, start_south_coap, fledge_ jdoc = json.loads(r) assert len(jdoc), "No data found" for stat in jdoc['statistics']: - is_matching = utils.check_datetime_format(stat['history_ts']) - assert is_matching == True, "History Timestamp format mismatched." + assert utils.validate_date_format(stat['history_ts']) is True, "History Timestamp format mismatched." read = [r['READINGS'] for r in jdoc['statistics']] assert 1 in read diff --git a/tests/system/python/helpers/utils.py b/tests/system/python/helpers/utils.py index 1d602dd447..01a3f59cf8 100644 --- a/tests/system/python/helpers/utils.py +++ b/tests/system/python/helpers/utils.py @@ -69,24 +69,22 @@ def delete_request(fledge_url, delete_url): return r -def check_datetime_format(datetime_str, format_str=None): +def validate_date_format(datetime_str, format_str=None): """ - Check if the given datetime string matches the specified format. + Check if the given datetime string matches the specified format Parameters: - datetime_str: The datetime string to check. - - format_str: The format string that the datetime string should match. + - format_str: The format string that the datetime string should match. By default, format '%Y-%m-%d %H:%M:%S.%f' Returns: - True if the string matches the format, False otherwise. """ try: - # Attempt to parse the datetime string with the specified format if format_str is None: format_str = "%Y-%m-%d %H:%M:%S.%f" datetime.strptime(datetime_str, format_str) return True except ValueError: - # Parsing failed; the string does not match the format return False From 4129c8e2ccc09c46aee8a9cd4baff2643930d4b1 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 21 Aug 2024 14:45:15 +0530 Subject: [PATCH 60/91] browser API readings timestamp verification added with different timezones Signed-off-by: ashish-jabble --- .../system/python/api/test_browser_assets.py | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/tests/system/python/api/test_browser_assets.py b/tests/system/python/api/test_browser_assets.py index 68bf6e15c2..9d9511ca63 100644 --- a/tests/system/python/api/test_browser_assets.py +++ b/tests/system/python/api/test_browser_assets.py @@ -25,6 +25,37 @@ SENSOR_VALUES = [1, 2, 3, 4, 5, 6] SOUTH_PLUGIN_NAME = 'dummyplugin' SERVICE_NAME = 'TestBrowserAPI' +DT_FORMAT = '%Y-%m-%d %H:%M:%S.%f' + + +def verify_utc_timestamp_with_different_timezones(timestamp_str): + + def _convert_utc_to_other_timezone(tz, hours, minutes): + from datetime import datetime, timedelta + import pytz + + # Parse the timestamp string into a naive datetime object + utc_dt = datetime.strptime(timestamp_str, DT_FORMAT) + # Add timezone info to the naive datetime object + utc_dt = pytz.utc.localize(utc_dt) + # Convert UTC to tz + converted_dt = utc_dt.astimezone(pytz.timezone(tz)) + # Define the expected offset + expected_offset = timedelta(hours=hours, minutes=minutes) + # Calculate the difference between the two offsets + actual_offset = converted_dt.utcoffset() - utc_dt.utcoffset() + assert actual_offset == expected_offset, "Expected offset {}, but got {}".format( + expected_offset, actual_offset) + + assert utils.validate_date_format(timestamp_str) is True, "Timestamp format mismatched." + + _convert_utc_to_other_timezone("Asia/Kolkata", 5, 30) + _convert_utc_to_other_timezone("America/Los_Angeles", -7, 0) + _convert_utc_to_other_timezone("America/New_York", -4, 0) + _convert_utc_to_other_timezone("Europe/London", 1, 0) + _convert_utc_to_other_timezone("Europe/Rome", 2, 0) + _convert_utc_to_other_timezone("Etc/UTC", 0, 0) + # TODO: Add more mappings if required class TestBrowserAssets: @@ -85,6 +116,7 @@ def test_get_asset(self, fledge_url): i = 0 for val in SENSOR_VALUES: assert {SENSOR: val} == jdoc[i]['reading'] + verify_utc_timestamp_with_different_timezones(jdoc[i]['timestamp']) i += 1 @pytest.mark.parametrize(("query", "expected_count", "expected_values"), [ @@ -93,7 +125,8 @@ def test_get_asset(self, fledge_url): ('?seconds=59', 2, SENSOR_VALUES[0:2]), ('?minutes=15', 4, SENSOR_VALUES[0:4]), ('?hours=4', 5, SENSOR_VALUES[0:5]), - ('?hours=20&minutes=20&seconds=59&limit=20', 2, SENSOR_VALUES[0:2]), # Verify that if a combination of hrs, min, sec is used, shortest period will apply + # Verify that if a combination of hrs, min, sec is used, shortest period will apply + ('?hours=20&minutes=20&seconds=59&limit=20', 2, SENSOR_VALUES[0:2]), ('?limit=&hours=&minutes=&seconds=', 6, SENSOR_VALUES) # In case of empty params, all values are returned ]) @@ -109,6 +142,7 @@ def test_get_asset_query(self, fledge_url, query, expected_count, expected_value i = 0 for item in expected_values: assert {SENSOR: item} == jdoc[i]['reading'] + verify_utc_timestamp_with_different_timezones(jdoc[i]['timestamp']) i += 1 @pytest.mark.parametrize("request_params, response_code, response_message", [ @@ -144,6 +178,7 @@ def test_get_asset_reading(self, fledge_url): i = 0 for val in SENSOR_VALUES: assert val == jdoc[i][SENSOR] + verify_utc_timestamp_with_different_timezones(jdoc[i]['timestamp']) i += 1 @pytest.mark.parametrize(("query", "expected_count", "expected_values"), [ @@ -152,7 +187,8 @@ def test_get_asset_reading(self, fledge_url): ('?seconds=59', 2, SENSOR_VALUES[0:2]), ('?minutes=15', 4, SENSOR_VALUES[0:4]), ('?hours=4', 5, SENSOR_VALUES[0:5]), - ('?hours=20&minutes=20&seconds=59&limit=20', 2, SENSOR_VALUES[0:2]), # Verify that if a combination of hrs, min, sec is used, shortest period will apply + # Verify that if a combination of hrs, min, sec is used, shortest period will apply + ('?hours=20&minutes=20&seconds=59&limit=20', 2, SENSOR_VALUES[0:2]), ('?limit=&hours=&minutes=&seconds=', 6, SENSOR_VALUES) # In case of empty params, all values are returned ]) @@ -168,6 +204,7 @@ def test_get_asset_readings_query(self, fledge_url, query, expected_count, expec i = 0 for item in expected_values: assert item == jdoc[i][SENSOR] + verify_utc_timestamp_with_different_timezones(jdoc[i]['timestamp']) i += 1 def test_get_asset_summary(self, fledge_url): @@ -186,7 +223,7 @@ def test_get_asset_summary(self, fledge_url): assert min(SENSOR_VALUES) == summary['min'] def test_get_asset_readings_summary_invalid_sensor(self, fledge_url): - """Test that browsing a non existing asset's data point summary gives blank min, max and average values""" + """Test that browsing a non-existing asset's data point summary gives blank min, max and average values""" conn = http.client.HTTPConnection(fledge_url) invalid_sensor = "invalid" conn.request("GET", '/fledge/asset/{}/{}/summary'.format(ASSET_NAME, invalid_sensor)) @@ -233,7 +270,8 @@ def test_get_asset_series(self, fledge_url): ('?seconds=59', 2, SENSOR_VALUES[0:2]), ('?minutes=15', 4, SENSOR_VALUES[0:4]), ('?hours=4', 5, SENSOR_VALUES[0:5]), - ('?hours=20&minutes=20&seconds=59&limit=20', 2, SENSOR_VALUES[0:2]), # Verify that if a combination of hrs, min, sec is used, shortest period will apply + # Verify that if a combination of hrs, min, sec is used, shortest period will apply + ('?hours=20&minutes=20&seconds=59&limit=20', 2, SENSOR_VALUES[0:2]), ('?limit=&hours=&minutes=&seconds=', 6, SENSOR_VALUES) # In case of empty params, all values are returned ]) From fe6a9da081d32e1cdf22be75e86b5a7b49ae6aae Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 21 Aug 2024 16:16:16 +0530 Subject: [PATCH 61/91] pytz test pip dep added Signed-off-by: ashish-jabble --- python/requirements-test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/python/requirements-test.txt b/python/requirements-test.txt index ed1a6df98b..c9595b31ec 100644 --- a/python/requirements-test.txt +++ b/python/requirements-test.txt @@ -5,6 +5,7 @@ pytest-asyncio==0.10.0 pytest-mock==1.10.3 pytest-cov==2.9.0 pytest-aiohttp==0.3.0 +pytz==2024.1 # Common - REST interface requests==2.20.0 From c0d56df2cce5c07ab5f5e172ddf1d2dd5c5b1322 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 22 Aug 2024 12:09:15 +0530 Subject: [PATCH 62/91] Content-Type fixes in Response header of backup download API Signed-off-by: ashish-jabble --- python/fledge/services/core/api/backup_restore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/fledge/services/core/api/backup_restore.py b/python/fledge/services/core/api/backup_restore.py index 6f7ae6f83b..696d0c0150 100644 --- a/python/fledge/services/core/api/backup_restore.py +++ b/python/fledge/services/core/api/backup_restore.py @@ -212,7 +212,7 @@ async def get_backup_download(request): _logger.error(ex, "Failed to download Backup file for ID: <{}>.".format(backup_id)) raise web.HTTPInternalServerError(reason=msg, body=json.dumps({"message": msg})) else: - return web.FileResponse(path=gz_path) + return web.FileResponse(path=gz_path, headers={'Content-Type': 'application/gzip'}) @has_permission("admin") From f56c7622b27f38f77a31b285f7c0cf41cc4ac05a Mon Sep 17 00:00:00 2001 From: Mohit04tomar <43023917+Mohit04tomar@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:32:37 +0530 Subject: [PATCH 63/91] Added Pytest xfail marker for FOGL-7712 (#1450) --- tests/system/python/packages/test_rule_data_availability.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/system/python/packages/test_rule_data_availability.py b/tests/system/python/packages/test_rule_data_availability.py index 2561685af1..4b74ec0548 100644 --- a/tests/system/python/packages/test_rule_data_availability.py +++ b/tests/system/python/packages/test_rule_data_availability.py @@ -197,6 +197,7 @@ def test_data_availability_single_audit(self, fledge_url, skip_verify_north_inte resp2 = utils.get_request(fledge_url, get_url) assert len(resp2['audit']) - len(resp1['audit']) == 1, "ERROR: NTFSN not triggered properly on CONCH" + @pytest.mark.xfail(reason="FOGL-7712") def test_data_availability_all_audit(self, fledge_url, add_south, skip_verify_north_interface, wait_time, retries): """ Test NTFSN triggered or not with all audit changes. on endpoint GET /fledge/audit From ac29b600cce3eaae75227c82bcb919aa2244ed76 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 22 Aug 2024 12:47:31 +0530 Subject: [PATCH 64/91] ts value column fixes in backups table for SQLite while uploading a backup Signed-off-by: ashish-jabble --- python/fledge/services/core/api/backup_restore.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/fledge/services/core/api/backup_restore.py b/python/fledge/services/core/api/backup_restore.py index 696d0c0150..7f400d6f10 100644 --- a/python/fledge/services/core/api/backup_restore.py +++ b/python/fledge/services/core/api/backup_restore.py @@ -338,8 +338,11 @@ async def upload_backup(request: web.Request) -> web.Response: # TODO: FOGL-5876 ts as per post param if given in payload # insert backup record entry in db full_file_name_path = "{}/{}".format(backup_path, backup_file_name) + # Get the current UTC date and format as 'YYYY-MM-DD HH:MM:SS' + from datetime import datetime + now_utc = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") payload = payload_builder.PayloadBuilder().INSERT( - file_name=full_file_name_path, ts="now()", type=1, status=2, exit_code=0).payload() + file_name=full_file_name_path, ts=now_utc, type=1, status=2, exit_code=0).payload() # audit trail entry storage = connect.get_storage_async() await storage.insert_into_tbl("backups", payload) From 2d84094d1b2985976097d152d891337dbceb4b86 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 27 Aug 2024 15:26:08 +0530 Subject: [PATCH 65/91] handling of scheduler not ready exception on GET north schedule API Signed-off-by: ashish-jabble --- python/fledge/services/core/api/north.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/fledge/services/core/api/north.py b/python/fledge/services/core/api/north.py index 9cdab722e4..1e4d01176e 100644 --- a/python/fledge/services/core/api/north.py +++ b/python/fledge/services/core/api/north.py @@ -15,6 +15,7 @@ from fledge.common.storage_client.payload_builder import PayloadBuilder from fledge.services.core import connect, server from fledge.services.core.scheduler.entities import Task +from fledge.services.core.scheduler.exceptions import NotReadyError from fledge.services.core.service_registry.service_registry import ServiceRegistry from fledge.services.core.service_registry.exceptions import DoesNotExist @@ -183,6 +184,11 @@ async def get_north_schedules(request): except (KeyError, ValueError) as e: # Handles KeyError of _get_sent_stats msg = str(e) return web.HTTPInternalServerError(reason=msg, body=json.dumps({"message": msg})) + except NotReadyError: + # This case will occur only if the client has sent the request and fledge server is not fully operational. + msg = "Failed to fetch north schedules information due to fledge server being down. Please try again later." + _logger.warning(msg) + return web.HTTPServiceUnavailable(reason=msg, body=json.dumps({"message": msg})) except Exception as ex: msg = str(ex) _logger.error(ex, "Failed to get the north schedules.") From aabc049fc2fc03b3c229f4c42198dd270b0f23f1 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 27 Aug 2024 17:06:14 +0530 Subject: [PATCH 66/91] message fixes Signed-off-by: ashish-jabble --- python/fledge/services/core/api/north.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/fledge/services/core/api/north.py b/python/fledge/services/core/api/north.py index 1e4d01176e..cca36be4e4 100644 --- a/python/fledge/services/core/api/north.py +++ b/python/fledge/services/core/api/north.py @@ -185,8 +185,8 @@ async def get_north_schedules(request): msg = str(e) return web.HTTPInternalServerError(reason=msg, body=json.dumps({"message": msg})) except NotReadyError: - # This case will occur only if the client has sent the request and fledge server is not fully operational. - msg = "Failed to fetch north schedules information due to fledge server being down. Please try again later." + # This case will occur if request is made while Fledge is not fully operational and/or being started. + msg = "Failed to fetch schedules information. Fledge is not fully operational and/or being started. Try again!" _logger.warning(msg) return web.HTTPServiceUnavailable(reason=msg, body=json.dumps({"message": msg})) except Exception as ex: From c87dfb20596dcca75b8e7c7ba8f3d1edc84c7d30 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 29 Aug 2024 16:55:56 +0530 Subject: [PATCH 67/91] computation fixes of categories list in PUT pipeline filter API for nested lists Signed-off-by: ashish-jabble --- python/fledge/services/core/api/filters.py | 20 ++++++++++++++++--- .../fledge/services/core/api/test_filters.py | 15 ++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/python/fledge/services/core/api/filters.py b/python/fledge/services/core/api/filters.py index ded547fa54..8d54088a70 100644 --- a/python/fledge/services/core/api/filters.py +++ b/python/fledge/services/core/api/filters.py @@ -8,7 +8,7 @@ import copy import aiohttp from aiohttp import web -from typing import List, Dict, Tuple +from typing import List, Dict, Tuple, Union from fledge.common import utils from fledge.common.common import _FLEDGE_ROOT @@ -502,8 +502,22 @@ async def _delete_configuration_category(storage: StorageClientAsync, key: str) config_mgr._cacheManager.remove(key) -def _diff(lst1: List[str], lst2: List[str]) -> List[str]: - return [v for v in lst2 if v not in lst1] +def _diff(list1: Union[List, str], list2: Union[List, str]) -> List: + """ Compute the difference between two lists and also handling of nested lists """ + + def flatten(lst: Union[List, str]) -> List: + """ Flatten a nested list into a single list """ + flat_list = [] + for item in lst: + if isinstance(item, list): + flat_list.extend(flatten(item)) + else: + flat_list.append(item) + return flat_list + + # Compute the difference: items in list2 but not in list1 + diff = [item for item in flatten(list2) if item not in flatten(list1)] + return diff def _delete_keys_from_dict(dict_del: Dict, lst_keys: List[str], deleted_values: Dict = {}, parent: str = None) \ diff --git a/tests/unit/python/fledge/services/core/api/test_filters.py b/tests/unit/python/fledge/services/core/api/test_filters.py index 08eb7c649c..961b4eed49 100644 --- a/tests/unit/python/fledge/services/core/api/test_filters.py +++ b/tests/unit/python/fledge/services/core/api/test_filters.py @@ -1028,6 +1028,21 @@ def test_delete_keys_from_dict(self): a, b = filters._delete_keys_from_dict(in_dict_del, lst_keys, deleted_values={}, parent=None) assert out_dict_del, deleted_values == (a, b) + @pytest.mark.parametrize("list1, list2, diff", [ + (['Rename'], ['Exp #1', 'Rename'], ['Exp #1']), + (['Meta_1'], ['Meta_1'], []), + (['Meta_1', 'Exp#1'], ['Meta_1'], []), + (['Exp'], ['Exp #1', 'Rename'], ['Exp #1', 'Rename']), + (['Exp', 'Exp #1', 'Rename'], ['Exp #1', 'Rename'], []), + ([['Rename #1'], 'Scale', 'Meta Data'], ['Asset'], ['Asset']), + ([['RE2'], 'RE3', 'PY35'], [['RE2', 'RE3'], 'PY35'], []), + ([['Py 35', 'Py#1 35'], 'Py35'], [['Py25', 'Py']], ['Py25', 'Py']), + ([['Rms #1'], 'Scale', 'Meta Data'], [['Scale'], 'Meta Data'], []) + ]) + async def test__diff(self, list1, list2, diff): + assert diff == filters._diff(list1, list2) + + async def test_delete_child_filters(self, mocker): # GIVEN user_name_mock = 'random1' From e4e01ce08cc2efc9943ae07c261146422317dabb Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 30 Aug 2024 12:24:45 +0530 Subject: [PATCH 68/91] allowed retrigger_time to 0 in notification API Signed-off-by: ashish-jabble --- .../fledge/services/core/api/notification.py | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/python/fledge/services/core/api/notification.py b/python/fledge/services/core/api/notification.py index 396c1a0e56..5c0cebb90f 100644 --- a/python/fledge/services/core/api/notification.py +++ b/python/fledge/services/core/api/notification.py @@ -186,16 +186,14 @@ async def post_notification(request): rule_config = data.get('rule_config', {}) delivery_config = data.get('delivery_config', {}) retrigger_time = data.get('retrigger_time', None) - try: - if retrigger_time: - if float(retrigger_time) > 0 and float(retrigger_time).is_integer(): - pass - else: + if retrigger_time is not None: + if not (isinstance(retrigger_time, str) and float(retrigger_time) >= 0 and isinstance( + float(retrigger_time), (int, float))): raise ValueError except ValueError: - raise ValueError('Invalid retrigger_time property in payload.') - + raise ValueError('retrigger_time value should be a number 0 or above. ' + 'Ensure this value is quoted as string e.g. "2.5".') if name is None or name.strip() == "": raise ValueError('Missing name property in payload.') if description is None: @@ -278,11 +276,9 @@ async def post_notification(request): "notification_type": notification_type, "enable": is_enabled, } - if retrigger_time: + if retrigger_time is not None: notification_config["retrigger_time"] = retrigger_time - await _update_configurations(config_mgr, name, notification_config, rule_config, delivery_config) - audit = AuditLogger(storage) await audit.information('NTFAD', {"name": name}) except ValueError as ex: @@ -336,16 +332,14 @@ async def put_notification(request): rule_config = data.get('rule_config', {}) delivery_config = data.get('delivery_config', {}) retrigger_time = data.get('retrigger_time', None) - try: - if retrigger_time: - if float(retrigger_time) > 0 and float(retrigger_time).is_integer(): - pass - else: + if retrigger_time is not None: + if not (isinstance(retrigger_time, str) and float(retrigger_time) >= 0 and isinstance( + float(retrigger_time), (int, float))): raise ValueError except ValueError: - raise ValueError('Invalid retrigger_time property in payload.') - + raise ValueError('retrigger_time value should be a number 0 or above. ' + 'Ensure this value is quoted as string e.g. "2.5".') if utils.check_reserved(notif) is False: raise ValueError('Invalid notification instance name.') if rule is not None and utils.check_reserved(rule) is False: @@ -436,7 +430,7 @@ async def put_notification(request): notification_config.update({"notification_type": notification_type}) if enabled is not None: notification_config.update({"enable": is_enabled}) - if retrigger_time: + if retrigger_time is not None: notification_config["retrigger_time"] = retrigger_time await _update_configurations(config_mgr, notif, notification_config, rule_config, delivery_config) except ValueError as e: From a18b3d4cbebbafc99e7ccef8dcc2db8a2467b415 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 2 Sep 2024 12:45:04 +0530 Subject: [PATCH 69/91] permissions user type check fixes in single item update configuration API Signed-off-by: ashish-jabble --- python/fledge/common/configuration_manager.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python/fledge/common/configuration_manager.py b/python/fledge/common/configuration_manager.py index 8577fa4126..5a4796dce3 100644 --- a/python/fledge/common/configuration_manager.py +++ b/python/fledge/common/configuration_manager.py @@ -1135,12 +1135,16 @@ async def set_category_item_value_entry(self, category_name, item_name, new_valu """ try: storage_value_entry = None + """ Note: Update reject to the properties with permissions property when the logged in user type is not + given in the list of permissions. """ + user_role_name = await self._check_updates_by_role(request) if category_name in self._cacheManager: if item_name not in self._cacheManager.cache[category_name]['value']: raise ValueError("No detail found for the category_name: {} and item_name: {}" .format(category_name, item_name)) storage_value_entry = self._cacheManager.cache[category_name]['value'][item_name] - + if user_role_name: + self._check_permissions(request, storage_value_entry, user_role_name) if storage_value_entry['value'] == new_value_entry: return else: @@ -1150,13 +1154,10 @@ async def set_category_item_value_entry(self, category_name, item_name, new_valu if storage_value_entry is None: raise ValueError("No detail found for the category_name: {} and item_name: {}" .format(category_name, item_name)) + if user_role_name: + self._check_permissions(request, storage_value_entry, user_role_name) if storage_value_entry == new_value_entry: return - """ Note: Update reject to the properties with permissions property when the logged in user type is not - given in the list of permissions. """ - user_role_name = await self._check_updates_by_role(request) - if user_role_name: - self._check_permissions(request, storage_value_entry, user_role_name) # Special case for enumeration field type handling if storage_value_entry['type'] == 'enumeration': if new_value_entry == '': From 659c58128295e8b2033013db2f58e4b834dce17d Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Tue, 3 Sep 2024 13:04:51 +0530 Subject: [PATCH 70/91] special case to open GET list of all users API for control user Signed-off-by: ashish-jabble --- python/fledge/common/web/middleware.py | 3 ++- .../python/api/test_endpoints_with_different_user_types.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index b37b98ba7c..badeab7a82 100644 --- a/python/fledge/common/web/middleware.py +++ b/python/fledge/common/web/middleware.py @@ -204,7 +204,8 @@ async def validate_requests(request): user_id = request.user['id'] # Only URL's which are specific meant for Admin user if not request.user_is_admin and request.method == 'GET': - if str(request.rel_url) == '/fledge/user': + # Special case: Allowed GET user for Control user + if int(request.user["role_id"]) != 5 and str(request.rel_url) == '/fledge/user': raise web.HTTPForbidden # Normal/Editor user if int(request.user["role_id"]) == 2 and request.method != 'GET': diff --git a/tests/system/python/api/test_endpoints_with_different_user_types.py b/tests/system/python/api/test_endpoints_with_different_user_types.py index 4a3882be72..2047a0b587 100644 --- a/tests/system/python/api/test_endpoints_with_different_user_types.py +++ b/tests/system/python/api/test_endpoints_with_different_user_types.py @@ -433,7 +433,7 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=5", 200), + ("GET", "/fledge/user", 200), ("GET", "/fledge/user?id=5", 200), ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 200), ("GET", "/fledge/user?id={}&username={}".format(5, CONTROL_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 5), 200), From 8acfb03c275e02d7353bfb8c04bee3178b876eed Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 3 Sep 2024 11:35:13 +0100 Subject: [PATCH 71/91] FOGL-9032 Add basic disk usage monitoring for the disks holding storage (#1449) * FOGL-9032 Add basic disk usage monitoring for the disk sholding storage data Signed-off-by: Mark Riddoch * Dont include disk_monitor in plugins/common unit test Signed-off-by: Mark Riddoch * Add logger Signed-off-by: Mark Riddoch * CMake changes Signed-off-by: Mark Riddoch * Exclude disk_monitor from services/storage/postgres/plugins/common Signed-off-by: Mark Riddoch * Address review comments Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- C/plugins/storage/common/disk_monitor.cpp | 163 ++++++++++++++++++ .../storage/common/include/disk_monitor.h | 35 ++++ .../sqlite/common/connection_manager.cpp | 47 ++++- .../common/include/connection_manager.h | 2 + .../sqlitelb/common/connection_manager.cpp | 37 +++- .../common/include/connection_manager.h | 2 + .../C/plugins/storage/common/CMakeLists.txt | 40 ++++- .../storage/postgres/etc/storage.json | 2 +- .../postgres/plugins/common/CMakeLists.txt | 2 +- 9 files changed, 320 insertions(+), 10 deletions(-) create mode 100644 C/plugins/storage/common/disk_monitor.cpp create mode 100644 C/plugins/storage/common/include/disk_monitor.h diff --git a/C/plugins/storage/common/disk_monitor.cpp b/C/plugins/storage/common/disk_monitor.cpp new file mode 100644 index 0000000000..84580e426a --- /dev/null +++ b/C/plugins/storage/common/disk_monitor.cpp @@ -0,0 +1,163 @@ +/* + * Fledge storage service. + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Mark Riddoch + */ +#include +#include +#include +#include + +using namespace std; + +/** + * Construct a disk space monitor class + * + * It monitors the free space on two path, since the storage service may use + * different locations for reading and configuration storage. If they are both + * the same file system then the monitor is only done once. + * + * If the free space falls below 5% a fatal error is written to the error log + * If it falls below 10% a warning is written advising that disk space should be released + * It attempts to predict when storage will become exhausted. If it is less then 14 days + * it will report this to the error log once a day. + * If it is less than 72 hours it will report this once per hour. + * + * All reporting is to the system log + * + * NB In an ideal world we would make the thresholds and reporting interval configurable, + * however we are running within the limited environment of a storage plugin and do not + * have access to the manaagement client or configuration subsystem. + */ +DiskSpaceMonitor::DiskSpaceMonitor(const string& path1, const string& path2) : + m_dbPath1(path1), m_dbPath2(path2), m_started(false), m_sameDevice(false), m_lastCheck(0), + m_lastPerc1(0.0), m_lastPerc2(0.0), m_lastPrediction1(0.0), m_lastPrediction2(0.0) +{ + m_logger = Logger::getLogger(); +} + +/** + * Called periodically to monitor the disk usage + * + * @param interval The number of seconds between calls + */ +void DiskSpaceMonitor::periodic(int interval) +{ + struct statfs stf1, stf2; + + if (!m_started) + { + if (statfs(m_dbPath1.c_str(), &stf1) != 0) + { + m_logger->error("Can't stafs %s", m_dbPath1.c_str()); + return; + } + if (statfs(m_dbPath2.c_str(), &stf2) != 0) + { + m_logger->error("Can't stafs %s", m_dbPath2.c_str()); + return; + } + if (memcmp(&stf1.f_fsid, &stf2.f_fsid, sizeof(fsid_t)) == 0) // Same filesystem + { + m_sameDevice = true; + } + m_started = true; + } + m_lastCheck += interval; + + if (m_lastCheck < CHECK_THRESHOLD) + { + // Do not check too frerquently + return; + } + m_lastCheck = 0; + + + if (statfs(m_dbPath1.c_str(), &stf1) == 0) + { + unsigned long freespace = (unsigned long)stf1.f_bavail; + unsigned long totalspace = (unsigned long)stf1.f_blocks; + + double perc = (double)(freespace * 100.0) / totalspace; + + if (perc < 5.0) + { + m_logger->fatal("Disk space is critically low. Urgent action required, continuing may result in data corruption"); + } + else if (perc < 10.0) + { + m_logger->error("Available disk space is becoming low, please consider releasing more disk space"); + } + if (m_lastPerc1 > 0.0) + { + double diff = m_lastPerc1 - perc; + if (diff > 0.0) + { + double prediction = (perc * CHECK_THRESHOLD)/ (3600.0 * diff); + if (prediction <= 72.0 && m_lastPrediction1 - prediction > 1.0) + { + m_logger->error("At current rates disk space will be exhausted in %.0f hours", prediction); + m_lastPrediction1 = prediction; + } + else if (prediction / 24.0 <= 14 && (m_lastPrediction1 == 0.0 || m_lastPrediction1 - prediction > 24.0)) + { + m_lastPrediction1 = prediction; + m_logger->warn("At current rates disk space will be exhausted in %.1f days", prediction / 24); + } + } + else + { + m_lastPrediction1 = 0.0; + } + } + m_lastPerc1 = perc; + + } + if (m_sameDevice) + { + return; + } + if (statfs(m_dbPath2.c_str(), &stf1) == 0) + { + unsigned long freespace = (unsigned long)stf1.f_bavail; + unsigned long totalspace = (unsigned long)stf1.f_blocks; + + double perc = (double)(freespace * 100.0) / totalspace; + + if (perc < 5.0) + { + m_logger->fatal("Disk space is critically low. Urgent action required, continuing may result in data corruption"); + } + else if (perc < 10.0) + { + m_logger->error("Available disk space is becoming low, please consider releasing more disk space"); + } + if (m_lastPerc2 > 0.0) + { + double diff = m_lastPerc2 - perc; + if (diff > 0.0) + { + double prediction = (perc * CHECK_THRESHOLD)/ (3600.0 * diff); + if (prediction <= 72.0 && (m_lastPrediction2 == 0.0 || m_lastPrediction2 - prediction > 1.0)) + { + m_logger->error("At current rates disk space will be exhausted in %.0f hours", prediction); + m_lastPrediction2 = prediction; + } + else if (prediction / 24.0 <= 14 && m_lastPrediction1 - prediction > 24.0) + { + m_lastPrediction2 = prediction; + m_logger->warn("At current rates disk space will be exhausted in %.1f days", prediction / 24); + } + } + else + { + m_lastPrediction2 = 0.0; + } + } + m_lastPerc2 = perc; + } +} diff --git a/C/plugins/storage/common/include/disk_monitor.h b/C/plugins/storage/common/include/disk_monitor.h new file mode 100644 index 0000000000..41c837c903 --- /dev/null +++ b/C/plugins/storage/common/include/disk_monitor.h @@ -0,0 +1,35 @@ +#ifndef _DISK_SPACE_MONITOR_H +#define _DISK_SPACE_MONITOR_H +/* + * Fledge storage service. + * + * Copyright (c) 2024 Dianomic Systems + * + * Released under the Apache 2.0 Licence + * + * Author: Mark Riddoch + */ +#include + +#define CHECK_THRESHOLD 300 // check every 5 minutes +/** + * A class to monitor the free disk space used to store + * the various storage databases + */ +class DiskSpaceMonitor { + public: + DiskSpaceMonitor(const std::string& db1, const std::string& db2); + void periodic(int interval); + private: + std::string m_dbPath1; + std::string m_dbPath2; + bool m_started; + bool m_sameDevice; + unsigned int m_lastCheck; + Logger *m_logger; + double m_lastPerc1; + double m_lastPerc2; + double m_lastPrediction1; + double m_lastPrediction2; +}; +#endif diff --git a/C/plugins/storage/sqlite/common/connection_manager.cpp b/C/plugins/storage/sqlite/common/connection_manager.cpp index d20141478d..64662588cb 100644 --- a/C/plugins/storage/sqlite/common/connection_manager.cpp +++ b/C/plugins/storage/sqlite/common/connection_manager.cpp @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include ConnectionManager *ConnectionManager::instance = 0; @@ -33,7 +36,8 @@ static void managerBackground(void *arg) */ ConnectionManager::ConnectionManager() : m_shutdown(false), m_vacuumInterval(6 * 60 * 60), - m_attachedDatabases(0) + m_attachedDatabases(0), + m_diskSpaceMonitor(0) { lastError.message = NULL; lastError.entryPoint = NULL; @@ -41,13 +45,41 @@ ConnectionManager::ConnectionManager() : m_shutdown(false), m_trace = true; else m_trace = false; + + std::string dbPath, dbPathReadings; + const char *defaultConnection = getenv("DEFAULT_SQLITE_DB_FILE"); + const char *defaultReadingsConnection = getenv("DEFAULT_SQLITE_DB_READINGS_FILE"); + if (defaultConnection == NULL) + { + // Set DB base path + dbPath = getDataDir(); + // Add the filename + dbPath += _DB_NAME; + } + else + { + dbPath = defaultConnection; + } + + if (defaultReadingsConnection == NULL) + { + // Set DB base path + dbPathReadings = getDataDir(); + // Add the filename + dbPathReadings += READINGS_DB_FILE_NAME; + } + else + { + dbPathReadings = defaultReadingsConnection; + } + + m_diskSpaceMonitor = new DiskSpaceMonitor(dbPath, dbPathReadings); + m_background = new std::thread(managerBackground, this); struct rlimit lim; getrlimit(RLIMIT_NOFILE, &lim); m_descriptorLimit = lim.rlim_cur; - - } /** @@ -60,6 +92,11 @@ void ConnectionManager::shutdown() shrinkPool(idle.size()); if (m_background) m_background->join(); + if (m_diskSpaceMonitor) + { + delete m_diskSpaceMonitor; + m_diskSpaceMonitor = NULL; + } } /** @@ -497,6 +534,8 @@ void ConnectionManager::background() while (!m_shutdown) { + if (m_diskSpaceMonitor) + m_diskSpaceMonitor->periodic(15); // Called with the interval we sleep for sleep(15); time_t tim = time(0); if (m_vacuumInterval && tim > nextVacuum) @@ -513,7 +552,7 @@ void ConnectionManager::background() * Determine if we can allow another database to be created and attached to all the * connections. * - * @return True if we can create anotehr database. + * @return True if we can create another database. */ bool ConnectionManager::allowMoreDatabases() { diff --git a/C/plugins/storage/sqlite/common/include/connection_manager.h b/C/plugins/storage/sqlite/common/include/connection_manager.h index 7a448ab8ca..a859a76410 100644 --- a/C/plugins/storage/sqlite/common/include/connection_manager.h +++ b/C/plugins/storage/sqlite/common/include/connection_manager.h @@ -21,6 +21,7 @@ #define DESCRIPTOR_THRESHOLD 75 // Percentage of descriptors that can be used on database connections class Connection; +class DiskSpaceMonitor; /** * Singleton class to manage SQLite3 connection pool @@ -70,6 +71,7 @@ class ConnectionManager { long m_vacuumInterval; unsigned int m_descriptorLimit; unsigned int m_attachedDatabases; + DiskSpaceMonitor *m_diskSpaceMonitor; }; #endif diff --git a/C/plugins/storage/sqlitelb/common/connection_manager.cpp b/C/plugins/storage/sqlitelb/common/connection_manager.cpp index 466b246cdf..2436a3e3b4 100644 --- a/C/plugins/storage/sqlitelb/common/connection_manager.cpp +++ b/C/plugins/storage/sqlitelb/common/connection_manager.cpp @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include ConnectionManager *ConnectionManager::instance = 0; @@ -25,7 +28,8 @@ static void managerBackground(void *arg) /** * Default constructor for the connection manager. */ -ConnectionManager::ConnectionManager() : m_shutdown(false), m_vacuumInterval(6 * 60 * 60), m_purgeBlockSize(10000) +ConnectionManager::ConnectionManager() : m_shutdown(false), m_vacuumInterval(6 * 60 * 60), m_purgeBlockSize(10000), + m_diskSpaceMonitor(NULL) { lastError.message = NULL; lastError.entryPoint = NULL; @@ -33,6 +37,35 @@ ConnectionManager::ConnectionManager() : m_shutdown(false), m_vacuumInterval(6 * m_trace = true; else m_trace = false; + + std::string dbPath, dbPathReadings; + const char *defaultConnection = getenv("DEFAULT_SQLITE_DB_FILE"); + const char *defaultReadingsConnection = getenv("DEFAULT_SQLITE_DB_READINGS_FILE"); + if (defaultConnection == NULL) + { + // Set DB base path + dbPath = getDataDir(); + // Add the filename + dbPath += _DB_NAME; + } + else + { + dbPath = defaultConnection; + } + + if (defaultReadingsConnection == NULL) + { + // Set DB base path + dbPathReadings = getDataDir(); + // Add the filename + dbPathReadings += READINGS_DB_FILE_NAME; + } + else + { + dbPathReadings = defaultReadingsConnection; + } + + m_diskSpaceMonitor = new DiskSpaceMonitor(dbPath, dbPathReadings); m_background = new std::thread(managerBackground, this); } @@ -200,6 +233,8 @@ void ConnectionManager::background() while (!m_shutdown) { + if (m_diskSpaceMonitor) + m_diskSpaceMonitor->periodic(15); // Called with the interval we sleep for sleep(15); time_t tim = time(0); if (m_vacuumInterval && tim > nextVacuum) diff --git a/C/plugins/storage/sqlitelb/common/include/connection_manager.h b/C/plugins/storage/sqlitelb/common/include/connection_manager.h index 214581285d..f767f74410 100644 --- a/C/plugins/storage/sqlitelb/common/include/connection_manager.h +++ b/C/plugins/storage/sqlitelb/common/include/connection_manager.h @@ -16,6 +16,7 @@ #include class Connection; +class DiskSpaceMonitor; /** * Singleton class to manage SQLite3 connection pool @@ -64,6 +65,7 @@ class ConnectionManager { bool m_persist; std::string m_filename; unsigned long m_purgeBlockSize; + DiskSpaceMonitor *m_diskSpaceMonitor; }; #endif diff --git a/tests/unit/C/plugins/storage/common/CMakeLists.txt b/tests/unit/C/plugins/storage/common/CMakeLists.txt index e752201cb6..7b108a2e41 100644 --- a/tests/unit/C/plugins/storage/common/CMakeLists.txt +++ b/tests/unit/C/plugins/storage/common/CMakeLists.txt @@ -6,6 +6,11 @@ set(GCOVR_PATH "$ENV{HOME}/.local/bin/gcovr") # Project configuration project(RunTests) +# Fledge libraries +set(COMMON_LIB common-lib) +set(SERVICE_COMMON_LIB services-common-lib) +set(PLUGINS_COMMON_LIB plugins-common-lib) + set(CMAKE_CXX_FLAGS "-std=c++11 -O0") include(CodeCoverage) @@ -17,17 +22,46 @@ include_directories(${GTEST_INCLUDE_DIRS}) include_directories(../../../../../../C/plugins/storage/common/include) include_directories(../../../../../../C/common/include) +# Exe creation +link_directories( + ${PROJECT_BINARY_DIR}/../../../../lib +) + + +# Find python3.x dev/lib package +find_package(PkgConfig REQUIRED) +if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + pkg_check_modules(PYTHON REQUIRED python3) +else() + find_package(Python3 COMPONENTS Interpreter Development) +endif() + +if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + link_directories(${PYTHON_LIBRARY_DIRS}) +else() + link_directories(${Python3_LIBRARY_DIRS}) +endif() + file(GLOB test_sources "../../../../../../C/plugins/storage/common/*.cpp") -set(common_sources "../../../../../../C/common/string_utils.cpp") # Link runTests with what we want to test and the GTest and pthread library -add_executable(RunTests ${test_sources} ${common_sources} tests.cpp) +add_executable(RunTests ${test_sources} tests.cpp) #setting BOOST_COMPONENTS to use pthread library only set(BOOST_COMPONENTS thread) find_package(Boost 1.53.0 COMPONENTS ${BOOST_COMPONENTS} REQUIRED) -target_link_libraries(RunTests ${GTEST_LIBRARIES} pthread ${COMMONLIB}) +target_link_libraries(RunTests ${COMMON_LIB}) +target_link_libraries(RunTests ${SERVICE_COMMON_LIB}) +target_link_libraries(RunTests ${PLUGINS_COMMON_LIB}) +target_link_libraries(RunTests ${GTEST_LIBRARIES} pthread ${COMMONLIB} ) + +# Add Python 3.x library +if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + target_link_libraries(${PROJECT_NAME} ${PYTHON_LIBRARIES}) +else() + target_link_libraries(${PROJECT_NAME} ${Python3_LIBRARIES}) +endif() setup_target_for_coverage_gcovr_html( NAME CoverageHtml diff --git a/tests/unit/C/services/storage/postgres/etc/storage.json b/tests/unit/C/services/storage/postgres/etc/storage.json index bfa3e91799..b588502ae0 100644 --- a/tests/unit/C/services/storage/postgres/etc/storage.json +++ b/tests/unit/C/services/storage/postgres/etc/storage.json @@ -1 +1 @@ -{"plugin":{"value":"postgres","default":"postgres","description":"The main storage plugin to load","type":"enumeration","options":["sqlite","sqlitelb","postgres"],"displayName":"Storage Plugin","order":"1"},"readingPlugin":{"value":"","default":"Use main plugin","description":"The storage plugin to load for readings data.","type":"enumeration","options":["Use main plugin","sqlite","sqlitelb","sqlitememory","postgres"],"displayName":"Readings Plugin","order":"2"},"threads":{"value":"1","default":"1","description":"The number of threads to run","type":"integer","displayName":"Database threads","order":"3"},"managedStatus":{"value":"false","default":"false","description":"Control if Fledge should manage the storage provider","type":"boolean","displayName":"Manage Storage","order":"4"},"port":{"value":"8080","default":"0","description":"The port to listen on","type":"integer","displayName":"Service Port","order":"5"},"managementPort":{"value":"1081","default":"0","description":"The management port to listen on.","type":"integer","displayName":"Management Port","order":"6"},"logLevel":{"value":"warning","default":"warning","description":"Minimum level of messages to log","type":"enumeration","displayName":"Log Level","options":["error","warning","info","debug"],"order":"7"}} \ No newline at end of file +{"plugin":{"value":"postgres","default":"postgres","description":"The main storage plugin to load","type":"enumeration","options":["sqlite","sqlitelb","postgres"],"displayName":"Storage Plugin","order":"1"},"readingPlugin":{"value":"","default":"Use main plugin","description":"The storage plugin to load for readings data.","type":"enumeration","options":["Use main plugin","sqlite","sqlitelb","sqlitememory","postgres"],"displayName":"Readings Plugin","order":"2"},"threads":{"value":"1","default":"1","description":"The number of threads to run","type":"integer","displayName":"Database threads","order":"3"},"managedStatus":{"value":"false","default":"false","description":"Control if Fledge should manage the storage provider","type":"boolean","displayName":"Manage Storage","order":"4"},"port":{"value":"8080","default":"0","description":"The port to listen on","type":"integer","displayName":"Service Port","order":"5"},"managementPort":{"value":"1081","default":"0","description":"The management port to listen on.","type":"integer","displayName":"Management Port","order":"6"},"logLevel":{"value":"warning","default":"warning","description":"Minimum level of messages to log","type":"enumeration","displayName":"Log Level","options":["error","warning","info","debug"],"order":"7"},"workerPool":{"value":"5","default":"5","description":"The number of threads to create in the thread pool used to execute operations against reading data","type":"integer","displayName":"Worker thread pool","minimum":"1","maximum":"10","order":"4"},"timeout":{"value":"60","default":"60","description":"Server request timeout, expressed in seconds","type":"integer","displayName":"Timeout","order":"9","minimum":"5","maximum":"3600"},"perfmon":{"description":"Track and store performance counters","type":"boolean","displayName":"Performance Counters","default":"false","value":"false","order":"10"}} \ No newline at end of file diff --git a/tests/unit/C/services/storage/postgres/plugins/common/CMakeLists.txt b/tests/unit/C/services/storage/postgres/plugins/common/CMakeLists.txt index cd2447a805..4809da7798 100644 --- a/tests/unit/C/services/storage/postgres/plugins/common/CMakeLists.txt +++ b/tests/unit/C/services/storage/postgres/plugins/common/CMakeLists.txt @@ -17,7 +17,7 @@ include_directories(${GTEST_INCLUDE_DIRS}) include_directories(../../../../../../../../C/plugins/storage/common/include) include_directories(../../../../../../../../C/common/include) -file(GLOB test_sources "../../../../../../../../C/plugins/storage/common/*.cpp") +file(GLOB test_sources "../../../../../../../../C/plugins/storage/common/sql_buffer.cpp") set(common_sources "../../../../../../../../C/common/string_utils.cpp") From d57bb2e2db7fc37e8b21b21ebfdff112d964d55a Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 3 Sep 2024 15:07:44 +0100 Subject: [PATCH 72/91] FOGL-8944 Alter branch completion criteria to allow for filter plugins that do (#1438) not forward data always and simply return Signed-off-by: Mark Riddoch --- C/common/pipeline_branch.cpp | 1 + C/services/north/data_load.cpp | 2 +- C/services/south/ingest.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/C/common/pipeline_branch.cpp b/C/common/pipeline_branch.cpp index 8c70e24984..79633a39d0 100644 --- a/C/common/pipeline_branch.cpp +++ b/C/common/pipeline_branch.cpp @@ -265,5 +265,6 @@ void PipelineBranch::handler() m_queue.pop(); lck.unlock(); m_branch[0]->ingest(readings); + m_pipeline->completeBranch(); } } diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index e63b61e5a2..081ac74ede 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -389,6 +389,7 @@ void DataLoad::bufferReadings(ReadingSet *readings) m_pipeline->execute(); // Pass readingSet to filter chain firstElement->ingest(readings); + m_pipeline->completeBranch(); // Main branch has completed m_pipeline->awaitCompletion(); return; } @@ -641,7 +642,6 @@ void DataLoad::pipelineEnd(OUTPUT_HANDLE *outHandle, unique_lock lck(load->m_qMutex); load->m_queue.push_back(readingSet); load->m_fetchCV.notify_all(); - load->m_pipeline->completeBranch(); } /** diff --git a/C/services/south/ingest.cpp b/C/services/south/ingest.cpp index dec1969391..69c0110bec 100755 --- a/C/services/south/ingest.cpp +++ b/C/services/south/ingest.cpp @@ -750,6 +750,7 @@ void Ingest::processQueue() // Pass readingSet to filter chain firstFilter->ingest(readingSet); + m_filterPipeline->completeBranch(); // Main branch has completed m_filterPipeline->awaitCompletion(); /* * If filtering removed all the readings then simply clean up m_data and @@ -1069,7 +1070,6 @@ void Ingest::useFilteredData(OUTPUT_HANDLE *outHandle, readingSet->clear(); delete readingSet; - ingest->m_filterPipeline->completeBranch(); } /** From e12716409c46678736aeaf309b643b407d093f36 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 4 Sep 2024 14:28:24 +0530 Subject: [PATCH 73/91] Forbidden case added in GET user profile API; only admin & control role user allowed to see any user profile Signed-off-by: ashish-jabble --- python/fledge/services/core/api/auth.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index dc1cccdd4d..9b564abfdf 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -319,6 +319,11 @@ async def get_user(request): user_name = request.query['username'].lower() if user_id or user_name: try: + if not request.is_auth_optional: + if int(request.user["role_id"]) not in [1, 5]: + if ((user_id is not None and int(request.user["id"]) != user_id) + or (user_name is not None and request.user["uname"] != user_name)): + raise web.HTTPForbidden user = await User.Objects.get(user_id, user_name) u = OrderedDict() u['userId'] = user.pop('id') From 3f34a4cfbd0c97f3aabb57fdc2fdf94384663383 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 4 Sep 2024 14:29:23 +0530 Subject: [PATCH 74/91] API integration tests for GET user Profile API for forbidden case added Signed-off-by: ashish-jabble --- .../test_endpoints_with_different_user_types.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/system/python/api/test_endpoints_with_different_user_types.py b/tests/system/python/api/test_endpoints_with_different_user_types.py index 4a3882be72..b2c298c7f6 100644 --- a/tests/system/python/api/test_endpoints_with_different_user_types.py +++ b/tests/system/python/api/test_endpoints_with_different_user_types.py @@ -110,10 +110,12 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=3", 200), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=3", 200), ("GET", "/fledge/user?id=2", 403), ("GET", "/fledge/user?username={}".format(VIEW_USERNAME), 200), + ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 403), ("GET", "/fledge/user?id={}&username={}".format(3, VIEW_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 3), 200), + ("GET", "/fledge/user?username=admin&id=1", 403), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/3/password", 500), ("GET", "/fledge/user/role", 403), # auth @@ -272,9 +274,10 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 403), ("GET", "/fledge/health/logging", 403), # user & roles - ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=4", 200), - ("GET", "/fledge/user?username={}".format(DATA_VIEW_USERNAME), 200), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=4", 200), ("GET", "/fledge/user?id=1", 403), + ("GET", "/fledge/user?username={}".format(DATA_VIEW_USERNAME), 200), ("GET", "/fledge/user?username=user", 403), ("GET", "/fledge/user?id={}&username={}".format(4, DATA_VIEW_USERNAME), 200), + ("GET", "/fledge/user?id=1&username=admin", 403), ("GET", "/fledge/user?username={}&id={}".format(DATA_VIEW_USERNAME, 4), 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/4/password", 500), ("GET", "/fledge/user/role", 403), @@ -433,10 +436,12 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=5", 200), - ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 200), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=5", 200), ("GET", "/fledge/user?id=1", 200), + ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 200), ("GET", "/fledge/user?username=admin", 200), ("GET", "/fledge/user?id={}&username={}".format(5, CONTROL_USERNAME), 200), ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 5), 200), + ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 3), 200), + ("GET", "/fledge/user?id={}&username={}".format(4, DATA_VIEW_USERNAME), 200), ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 401), ("PUT", "/fledge/user/5/password", 500), ("GET", "/fledge/user/role", 403), # auth From 8f887a585368281be1f0c6495a8502c67c02d09b Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 5 Sep 2024 15:02:13 +0100 Subject: [PATCH 75/91] FOGL-9091 Improve error reporting and rate limit error logs when we have (#1461) * FOGL-9091 Improve error reporting and rate limit error logs when we have an issue accessing the filesystem status Signed-off-by: Mark Riddoch * Fix typo Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- C/plugins/storage/common/disk_monitor.cpp | 32 +++++++++++++++++-- .../storage/common/include/disk_monitor.h | 4 +++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/C/plugins/storage/common/disk_monitor.cpp b/C/plugins/storage/common/disk_monitor.cpp index 84580e426a..ba1d2caa02 100644 --- a/C/plugins/storage/common/disk_monitor.cpp +++ b/C/plugins/storage/common/disk_monitor.cpp @@ -35,7 +35,7 @@ using namespace std; */ DiskSpaceMonitor::DiskSpaceMonitor(const string& path1, const string& path2) : m_dbPath1(path1), m_dbPath2(path2), m_started(false), m_sameDevice(false), m_lastCheck(0), - m_lastPerc1(0.0), m_lastPerc2(0.0), m_lastPrediction1(0.0), m_lastPrediction2(0.0) + m_lastPerc1(0.0), m_lastPerc2(0.0), m_lastPrediction1(0.0), m_lastPrediction2(0.0), m_reported(0) { m_logger = Logger::getLogger(); } @@ -51,14 +51,40 @@ void DiskSpaceMonitor::periodic(int interval) if (!m_started) { + // We have not yet started to monitor the disk usage. + // Do the initial statfs calls to see if the configuration + // and readings are on the same filesystem. If they are we + // only monitor one of them + // + // If the statfs fails log it and do not start monitoring. The + // rate at which logs are created is limited to prevent flooding + // the error log. if (statfs(m_dbPath1.c_str(), &stf1) != 0) { - m_logger->error("Can't stafs %s", m_dbPath1.c_str()); + if (m_reported == 0) + { + m_logger->error("Can't statfs %s, %s. Disk space monitoring is disabled", + m_dbPath1.c_str(), strerror(errno)); + m_reported++; + } + else if (++m_reported > FAILED_DISK_MONITOR_REPORT_INTERVAL) + { + m_reported = 0; + } return; } if (statfs(m_dbPath2.c_str(), &stf2) != 0) { - m_logger->error("Can't stafs %s", m_dbPath2.c_str()); + if (m_reported == 0) + { + m_logger->error("Can't statfs %s, %s. Disk space monitoring is disabled", + m_dbPath2.c_str(), strerror(errno)); + m_reported++; + } + else if (++m_reported > FAILED_DISK_MONITOR_REPORT_INTERVAL) + { + m_reported = 0; + } return; } if (memcmp(&stf1.f_fsid, &stf2.f_fsid, sizeof(fsid_t)) == 0) // Same filesystem diff --git a/C/plugins/storage/common/include/disk_monitor.h b/C/plugins/storage/common/include/disk_monitor.h index 41c837c903..c88f82b3de 100644 --- a/C/plugins/storage/common/include/disk_monitor.h +++ b/C/plugins/storage/common/include/disk_monitor.h @@ -12,6 +12,9 @@ #include #define CHECK_THRESHOLD 300 // check every 5 minutes + +#define FAILED_DISK_MONITOR_REPORT_INTERVAL 600 // Interval between loggign failure to stat the filesystem (10 minutes) + /** * A class to monitor the free disk space used to store * the various storage databases @@ -31,5 +34,6 @@ class DiskSpaceMonitor { double m_lastPerc2; double m_lastPrediction1; double m_lastPrediction2; + int m_reported; }; #endif From a136cd264760aed990ac9a1ae1bea0ec55a06fff Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 9 Sep 2024 15:03:31 +0530 Subject: [PATCH 76/91] parent-child relationship and display name fixes for backup and restore in PostgreSQL engine Signed-off-by: ashish-jabble --- python/fledge/plugins/storage/postgres/backup_restore/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/fledge/plugins/storage/postgres/backup_restore/lib.py b/python/fledge/plugins/storage/postgres/backup_restore/lib.py index 9420ef90e2..59c629b31c 100644 --- a/python/fledge/plugins/storage/postgres/backup_restore/lib.py +++ b/python/fledge/plugins/storage/postgres/backup_restore/lib.py @@ -884,7 +884,9 @@ def _retrieve_configuration_from_manager(self): _event_loop.run_until_complete(cfg_manager.create_category( self._CONFIG_CATEGORY_NAME, self._CONFIG_DEFAULT, - self._CONFIG_CATEGORY_DESCRIPTION)) + self._CONFIG_CATEGORY_DESCRIPTION, display_name="Backup & Restore")) + _event_loop.run_until_complete(cfg_manager.create_child_category( + "Utilities", [self._CONFIG_CATEGORY_NAME])) self._config_from_manager = _event_loop.run_until_complete(cfg_manager.get_category_all_items (self._CONFIG_CATEGORY_NAME)) From 1209a5caeb1f0b78a71149e51c661978950c8e1a Mon Sep 17 00:00:00 2001 From: gnandan <111729765+gnandan@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:20:26 +0530 Subject: [PATCH 77/91] FOGL-9061: modified asset to have html encoded value (#1463) * FOGL-9061: modified asset to have html encoded value Signed-off-by: nandan --- python/fledge/common/storage_client/storage_client.py | 3 ++- .../fledge/common/storage_client/test_storage_client.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/python/fledge/common/storage_client/storage_client.py b/python/fledge/common/storage_client/storage_client.py index dfff9234cf..b146ea0a1e 100644 --- a/python/fledge/common/storage_client/storage_client.py +++ b/python/fledge/common/storage_client/storage_client.py @@ -581,7 +581,8 @@ async def purge(self, age=None, sent_id=0, size=None, flag=None, asset=None): if flag: put_url += "&flags={}".format(flag.lower()) if asset is not None: - put_url = '/storage/reading/purge?asset={}'.format(asset) + import urllib.parse + put_url = '/storage/reading/purge?asset={}'.format(urllib.parse.quote(asset)) url = 'http://' + self._base_url + put_url async with aiohttp.ClientSession() as session: diff --git a/tests/unit/python/fledge/common/storage_client/test_storage_client.py b/tests/unit/python/fledge/common/storage_client/test_storage_client.py index d87f921d43..ec3b055f1e 100644 --- a/tests/unit/python/fledge/common/storage_client/test_storage_client.py +++ b/tests/unit/python/fledge/common/storage_client/test_storage_client.py @@ -766,4 +766,8 @@ async def test_purge(self, event_loop): response = await rsc.purge(**kwargs) assert 1 == response["called"] + kwargs = dict(asset="sin #1") + response = await rsc.purge(**kwargs) + assert 1 == response["called"] + await fake_storage_srvr.stop() From b55ad0da084bf1b717128323f2d72e00ef1b974a Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Wed, 25 Sep 2024 14:31:11 +0530 Subject: [PATCH 78/91] Fixes for executing the entry point when anonymous access is enabled Signed-off-by: ashish-jabble --- .../core/api/control_service/entrypoint.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/python/fledge/services/core/api/control_service/entrypoint.py b/python/fledge/services/core/api/control_service/entrypoint.py index 0304bfe1f7..53141151d7 100644 --- a/python/fledge/services/core/api/control_service/entrypoint.py +++ b/python/fledge/services/core/api/control_service/entrypoint.py @@ -407,7 +407,7 @@ async def update(request: web.Request) -> web.Response: async def update_request(request: web.Request) -> web.Response: - """API control entry points can be called with PUT operation to URL form + """API control entrypoints can be called with PUT operation to URL form :Example: curl -sX PUT http://localhost:8081/fledge/control/request/SetLatheSpeed -d '{"distance": "13"}' """ @@ -424,15 +424,17 @@ async def update_request(request: web.Request) -> web.Response: ep_info = await _get_entrypoint(name) username = "Anonymous" if request.user is not None: - # Admin and Control role users can always call entrypoints. - # For others, it must be matched from the list of allowed users + """ Admin and Control role users can always execute entrypoint. + With anonymous - Editor role can execute entrypoint. + When anonymous set to False, then + a) Editor role, it must be matched from the list of allowed users. + b) Other roles like viewer and dataviewer, endpoint is restricted. + """ if request.user["role_id"] not in (1, 5): - allowed_user = [r for r in ep_info['allow']] - # TODO: FOGL-8037 - If allowed user list is empty then should we allow to proceed with update request? - # How about viewer and data viewer role access to this route? - # as of now simply reject with Forbidden 403 - if request.user["uname"] not in allowed_user: - raise ValueError("Operation is not allowed for the {} user.".format(request.user['uname'])) + if ep_info and not ep_info['anonymous']: + allowed_user = [r for r in ep_info['allow']] + if request.user["uname"] not in allowed_user: + raise ValueError("Operation is not allowed for the {} user.".format(request.user['uname'])) username = request.user["uname"] data = await request.json() From e6753257eca0d20a10f393b507739806401d8718 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 30 Sep 2024 15:18:22 +0530 Subject: [PATCH 79/91] API statistics schedule time fixes in tests Signed-off-by: ashish-jabble --- tests/system/python/api/test_statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/python/api/test_statistics.py b/tests/system/python/api/test_statistics.py index d8a9b73a0f..ca57a651ce 100644 --- a/tests/system/python/api/test_statistics.py +++ b/tests/system/python/api/test_statistics.py @@ -150,7 +150,7 @@ def test_statistics_history_with_service_enabled(self, start_south_coap, fledge_ assert 1 == stats['READINGS'] # Allow stats collector schedule to run i.e. by default 15s - time.sleep(wait_time * 2 + 1) + time.sleep(wait_time * 3) # check stats history conn.request("GET", '/fledge/statistics/history') From 0a71e0385b0c929dc23a6a635083947f3eccc918 Mon Sep 17 00:00:00 2001 From: pintomax Date: Mon, 30 Sep 2024 12:05:36 +0200 Subject: [PATCH 80/91] FOGL-9143: remove "\n" string from JSON object (#1467) FOGL-9143: remove "\n" string from JSON object converted from string --- C/common/config_category.cpp | 3 +++ tests/unit/C/common/test_config_category.cpp | 14 ++++++++++++++ .../unit/C/common/test_default_config_category.cpp | 13 +++++++++++++ 3 files changed, 30 insertions(+) diff --git a/C/common/config_category.cpp b/C/common/config_category.cpp index 46357ba542..18b845af1f 100755 --- a/C/common/config_category.cpp +++ b/C/common/config_category.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace std; @@ -1414,6 +1415,7 @@ ConfigCategory::CategoryItem::CategoryItem(const string& name, // If it's not a real eject, check the string buffer it is: if (!item["value"].IsObject()) { + boost::replace_all(m_value, "\\n", ""); Document check; check.Parse(m_value.c_str()); if (check.HasParseError()) @@ -1522,6 +1524,7 @@ ConfigCategory::CategoryItem::CategoryItem(const string& name, // If it's not a real eject, check the string buffer it is: if (!item["default"].IsObject()) { + boost::replace_all(m_default, "\\n", ""); Document check; check.Parse(m_default.c_str()); if (check.HasParseError()) diff --git a/tests/unit/C/common/test_config_category.cpp b/tests/unit/C/common/test_config_category.cpp index 5a36334001..8047e5c80f 100644 --- a/tests/unit/C/common/test_config_category.cpp +++ b/tests/unit/C/common/test_config_category.cpp @@ -730,3 +730,17 @@ TEST(CategoryTest, kvlistObjectItem) ASSERT_EQ(0, v["a"].compare("{\"one\":\"first\"}")); ASSERT_EQ(0, v["b"].compare("{\"two\":\"second\"}")); } + +// Config category with \n in both default and value +string categoryJsonLF = R"({"plugin": {"description": "Sinusoid Poll Plugin which implements sine wave with data points", "type": "string", "default": "sinusoid", "readonly": "true"}, "assetName": {"description": "Name of Asset", "type": "string", "default": "sinusoid", "displayName": "Asset name", "mandatory": "true"}, "writemap": {"description": "Map of tags", "displayName": "Tags to write", "type": "JSON", "default": "{\n\"tags\": [\n{\n\"name\": \"PLCTAG\",\n\"type\": \"UINT32\",\n\"program\": \"\"\n}]\n\n}", "value" : "{\n\"tags\": [\n{\n\"name\": \"PLCTAG\",\n\"type\": \"UINT32\",\n\"program\": \"\"\n, \"asset\": \"Asset\",\n\"datapoint\": \"Datapoint\"\n}\n]\n}"}})"; +string defaultJsonClear = R"({"tags": [{"name": "PLCTAG","type": "UINT32","program": ""}]})"; +string valueJsonClear = R"({"tags": [{"name": "PLCTAG","type": "UINT32","program": "", "asset": "Asset","datapoint": "Datapoint"}]})"; + +TEST(CategoryTestLF, toJSONWithoutLF) +{ + DefaultConfigCategory confCategory("testLF", categoryJsonLF); + confCategory.setDescription("Test description"); + + ASSERT_EQ(0, confCategory.getDefault("writemap").compare(defaultJsonClear)); + ASSERT_EQ(0, confCategory.getValue("writemap").compare(valueJsonClear)); +} diff --git a/tests/unit/C/common/test_default_config_category.cpp b/tests/unit/C/common/test_default_config_category.cpp index bc3caba683..a061f1dffe 100644 --- a/tests/unit/C/common/test_default_config_category.cpp +++ b/tests/unit/C/common/test_default_config_category.cpp @@ -342,3 +342,16 @@ TEST(DefaultCategoryTestQuoted, toJSONQuotedSpecial) // Only "default" value in the output ASSERT_EQ(0, confCategory.toJSON().compare(default_json_quotedSpecial)); } + +// Default config category with \n in default JSON +string jsonLF = R"({"plugin": {"description": "Sinusoid Poll Plugin which implements sine wave with data points", "type": "string", "default": "sinusoid", "readonly": "true"}, "assetName": {"description": "Name of Asset", "type": "string", "default": "sinusoid", "displayName": "Asset name", "mandatory": "true"}, "writemap": {"description": "Map of tags", "displayName": "Tags to write", "type": "JSON", "default": "{\n \"tags\": [\n {\n \"name\": \"PLCTAG\",\n \"type\": \"UINT32\",\n \"program\": \"\"\n }\n ]\n}"}})"; +string jsonClear = R"({ "tags": [ { "name": "PLCTAG", "type": "UINT32", "program": "" } ]})"; + +TEST(DefaultCategoryTestLF, toJSONWithoutLF) +{ + DefaultConfigCategory confCategory("testLF", jsonLF); + confCategory.setDescription("Test description"); + + // Only "default" value in the output + ASSERT_EQ(0, confCategory.getDefault("writemap").compare(jsonClear)); +} From 7e0929d0978e006ada143112d4a924c23e845a7b Mon Sep 17 00:00:00 2001 From: Ray Verhoeff Date: Mon, 7 Oct 2024 16:09:29 -0400 Subject: [PATCH 81/91] Make OMF Data Action code configurable Signed-off-by: Ray Verhoeff --- C/plugins/north/OMF/include/omf.h | 3 +++ C/plugins/north/OMF/include/omfinfo.h | 2 ++ C/plugins/north/OMF/omf.cpp | 6 ++---- C/plugins/north/OMF/omfinfo.cpp | 22 ++++++++++++++++++++++ C/plugins/north/OMF/plugin.cpp | 8 ++++++++ 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/C/plugins/north/OMF/include/omf.h b/C/plugins/north/OMF/include/omf.h index 0c97c0184a..177165ae3c 100644 --- a/C/plugins/north/OMF/include/omf.h +++ b/C/plugins/north/OMF/include/omf.h @@ -197,6 +197,8 @@ class OMF void setDelimiter(const std::string &delimiter) {m_delimiter = delimiter;}; + void setDataActionCode(const std::string &actionCode) {m_dataActionCode = actionCode;}; + // Get saved OMF formats std::string getFormatType(const std::string &key) const; @@ -389,6 +391,7 @@ class OMF std::string m_DefaultAFLocation; bool m_sendFullStructure; // If disabled the AF hierarchy is not created. std::string m_delimiter; + std::string m_dataActionCode; // Asset Framework Hierarchy Rules handling - Metadata MAP // Documentation: https://fledge-iot.readthedocs.io/en/latest/plugins/fledge-north-OMF/index.html?highlight=hierarchy#asset-framework-hierarchy-rules diff --git a/C/plugins/north/OMF/include/omfinfo.h b/C/plugins/north/OMF/include/omfinfo.h index b0b67aae6c..56d691951f 100644 --- a/C/plugins/north/OMF/include/omfinfo.h +++ b/C/plugins/north/OMF/include/omfinfo.h @@ -114,6 +114,7 @@ class OMFInformation { int PIWebAPIGetVersion(bool logMessage = true); int EDSGetVersion(); void SetOMFVersion(); + void CheckDataActionCode(); std::string OCSRetrieveAuthToken(); OMF_ENDPOINT identifyPIServerEndpoint(); std::string saveSentDataTypes(); @@ -138,6 +139,7 @@ class OMFInformation { unsigned int m_timeout; // connect and operation timeout string m_path; // PI Server application path string m_delimiter; // delimiter between Asset and Datapoint in PI data stream names + string m_dataActionCode; // Action code to use for OMF Data posts: update or create long m_typeId; // OMF protocol type-id prefix string m_producerToken; // PI Server connector token string m_formatNumber; // OMF protocol Number format diff --git a/C/plugins/north/OMF/omf.cpp b/C/plugins/north/OMF/omf.cpp index 2f41cbc6c8..2ce937f22c 100755 --- a/C/plugins/north/OMF/omf.cpp +++ b/C/plugins/north/OMF/omf.cpp @@ -1437,8 +1437,7 @@ uint32_t OMF::sendToServer(const vector& readings, */ // Create header for Readings data - std::string action = (this->m_OMFVersion.compare("1.2") == 0) ? "update" : "create"; - vector> readingData = OMF::createMessageHeader("Data", action); + vector> readingData = OMF::createMessageHeader("Data", m_dataActionCode); if (compression) readingData.push_back(pair("compression", "gzip")); @@ -4872,8 +4871,7 @@ bool OMF::sendAFLinks(Reading &reading, OMFHints *hints) try { - std::string action = (this->m_OMFVersion.compare("1.2") == 0) ? "update" : "create"; - vector> messageHeader = OMF::createMessageHeader("Data", action); + vector> messageHeader = OMF::createMessageHeader("Data", m_dataActionCode); afLinks = "[" + afLinks + "]"; int res = m_sender.sendRequest("POST", diff --git a/C/plugins/north/OMF/omfinfo.cpp b/C/plugins/north/OMF/omfinfo.cpp index cd4f73522a..5032ac97b4 100644 --- a/C/plugins/north/OMF/omfinfo.cpp +++ b/C/plugins/north/OMF/omfinfo.cpp @@ -264,6 +264,9 @@ OMFInformation::OMFInformation(ConfigCategory *config) : m_sender(NULL), m_omf(N } } + // Set the Action Code for OMF Data posts: update or create + m_dataActionCode = config->itemExists("OMFDataActionCode") ? config->getValue("OMFDataActionCode") : "update"; + { // NamingScheme handling if(NamingScheme.compare("Concise") == 0) @@ -389,6 +392,7 @@ void OMFInformation::start(const string& storedData) if (httpCode >= 200 && httpCode < 400) { SetOMFVersion(); + CheckDataActionCode(); Logger::getLogger()->info("%s connected to %s OMF Version: %s", m_RestServerVersion.c_str(), m_hostAndPort.c_str(), m_omfversion.c_str()); m_connected = true; @@ -514,6 +518,7 @@ uint32_t OMFInformation::send(const vector& readings) m_omf->setSendFullStructure(m_sendFullStructure); m_omf->setDelimiter(m_delimiter); + m_omf->setDataActionCode(m_dataActionCode); // Set PIServerEndpoint configuration m_omf->setNamingScheme(m_NamingScheme); @@ -994,6 +999,22 @@ void OMFInformation::SetOMFVersion() } } +/** + * Check the Action code for OMF Data messages. + * This method changes the Action code only if 'update' is specified for an OMF version too old to support it. + */ +void OMFInformation::CheckDataActionCode() +{ + if (!m_omfversion.empty()) + { + if ((m_omfversion.compare("1.2") != 0) && (m_dataActionCode.compare("update") == 0)) + { + Logger::getLogger()->warn("OMF Version %s does not support Data Action Code %s; setting to 'create'", m_omfversion.c_str(), m_dataActionCode.c_str()); + m_dataActionCode = "create"; + } + } +} + /** * Calls the OCS API to retrieve the authentication token * @@ -1400,6 +1421,7 @@ bool OMFInformation::IsPIWebAPIConnected() { m_connected = true; SetOMFVersion(); + CheckDataActionCode(); if (lastConnected == false) { Logger::getLogger()->warn("%s reconnected to %s OMF Version: %s", diff --git a/C/plugins/north/OMF/plugin.cpp b/C/plugins/north/OMF/plugin.cpp index 52425898c7..9270e2c4d0 100755 --- a/C/plugins/north/OMF/plugin.cpp +++ b/C/plugins/north/OMF/plugin.cpp @@ -169,6 +169,14 @@ const char *PLUGIN_DEFAULT_CONFIG_INFO = QUOTE( "order": "10", "displayName": "Data Stream Name Delimiter" }, + "OMFDataActionCode": { + "description": "OMF Action Code to use when POSTing OMF Data messages", + "type": "enumeration", + "options":["update", "create"], + "default": "update", + "order": "11", + "displayName": "Action Code for Data Messages" + }, "OMFRetrySleepTime": { "description": "Seconds between each retry for the communication with the OMF PI Connector Relay, NOTE : the time is doubled at each attempt.", "type": "integer", From 92e7283eb6e5d0956daf02c164e8146cef44b720 Mon Sep 17 00:00:00 2001 From: Ray Verhoeff Date: Mon, 7 Oct 2024 16:43:46 -0400 Subject: [PATCH 82/91] Added Action Code for Data Messages Add description of new configuration item "Action Code for Data Messages." Signed-off-by: Ray Verhoeff --- docs/OMF.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/OMF.rst b/docs/OMF.rst index cac115c837..3cf2974021 100644 --- a/docs/OMF.rst +++ b/docs/OMF.rst @@ -151,6 +151,20 @@ The *Basic* tab contains the most commonly modified items - **Data Stream Name Delimiter**: The plugin creates Container names by concatenating Asset and Datapoint names separated by this single-character delimiter. The default delimiter is a dot ("."). + - **Action Code for Data Messages**: Defines the action code in the HTTP header when sending OMF Data messages. + + All OMF messages must have an *action* code in the HTTP header which defines how the server should process the OMF message. + For Data messages, the default action code is *update* which means that the server should update the data value if there is already a value at the passed timestamp. + If there is no value at the passed timestamp, the data value is inserted into the server's data archive. + If the passed data value is newer than the server's snapshot, the new value is processed by the server's compression algorithm. + The action code of *update* is the default and should generally be left unchanged. + + The one exception is if the PI Buffer Subsystem is used to buffer data sent to the PI Data Archive. + Because of an issue with the PI Buffer Subsystem, OMF data sent with an action code of *update* is converted to the PI Data Archive's internal *replace* storage code. + The *replace* storage code causes the PI Data Archive's compression algorithm to be bypassed. + When using the PI Buffer Subsystem, set the action code to *create* which will allow new data to be compressed normally. + One disadvantage of the *create* action code is that multiple values with the same timestamp will all be stored. + Asset Framework ~~~~~~~~~~~~~~~ From 082a096adc427ec4a84503f7048a79bbf7923eca Mon Sep 17 00:00:00 2001 From: Ray Verhoeff Date: Mon, 7 Oct 2024 17:11:08 -0400 Subject: [PATCH 83/91] New Basic tab screenshot Added Action Code for Data Messages Signed-off-by: Ray Verhoeff --- docs/images/OMF_Default.jpg | Bin 46991 -> 44718 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/OMF_Default.jpg b/docs/images/OMF_Default.jpg index bfbd18d77676069600d0b4e357eba0bdab150905..8124df72db91f424647dced94571f89c2632438a 100644 GIT binary patch literal 44718 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:j>8{sceXNyrw0Ffy9%FWb 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 literal 46991 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 Date: Mon, 14 Oct 2024 13:24:36 +0100 Subject: [PATCH 84/91] FOGL-9037 Add a monitoring section to the documentation (#1471) * FOGL-9037 Add monitoring section to documentation Signed-off-by: Mark Riddoch * Checkpoint Signed-off-by: Mark Riddoch * FOGL-9037 Add moitoring section Signed-off-by: Mark Riddoch * Address review comments Signed-off-by: Mark Riddoch * Two more review comments resolved Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- docs/images/CONCH_available.jpg | Bin 0 -> 45528 bytes docs/images/CONCH_message.jpg | Bin 0 -> 14360 bytes docs/images/CONCH_slack.jpg | Bin 0 -> 56494 bytes docs/images/MonitorDiskUsage.jpg | Bin 0 -> 62601 bytes docs/images/MonitorMatch.jpg | Bin 0 -> 52851 bytes docs/images/MonitorNorthRate.jpg | Bin 0 -> 61032 bytes docs/images/MonitorTrigger.jpg | Bin 0 -> 37170 bytes docs/images/MonitorWatchDog.jpg | Bin 0 -> 50228 bytes docs/images/MonitorZendesk.jpg | Bin 0 -> 77867 bytes docs/images/MonitoredBuffered.jpg | Bin 0 -> 46178 bytes docs/images/north_audit.jpg | Bin 0 -> 6539 bytes docs/images/north_change_log.jpg | Bin 0 -> 25266 bytes docs/index.rst | 1 + docs/monitoring/configuration.rst | 206 ++++++++++++++++++++++++++++++ docs/monitoring/flow.rst | 68 ++++++++++ docs/monitoring/index.rst | 12 ++ docs/monitoring/introduction.rst | 17 +++ docs/monitoring/quality.rst | 46 +++++++ docs/monitoring/resources.rst | 46 +++++++ docs/monitoring/service.rst | 30 +++++ 20 files changed, 426 insertions(+) create mode 100644 docs/images/CONCH_available.jpg create mode 100644 docs/images/CONCH_message.jpg create mode 100644 docs/images/CONCH_slack.jpg create mode 100644 docs/images/MonitorDiskUsage.jpg create mode 100644 docs/images/MonitorMatch.jpg create mode 100644 docs/images/MonitorNorthRate.jpg create mode 100644 docs/images/MonitorTrigger.jpg create mode 100644 docs/images/MonitorWatchDog.jpg create mode 100644 docs/images/MonitorZendesk.jpg create mode 100644 docs/images/MonitoredBuffered.jpg create mode 100644 docs/images/north_audit.jpg create mode 100644 docs/images/north_change_log.jpg create mode 100644 docs/monitoring/configuration.rst create mode 100644 docs/monitoring/flow.rst create mode 100644 docs/monitoring/index.rst create mode 100644 docs/monitoring/introduction.rst create mode 100644 docs/monitoring/quality.rst create mode 100644 docs/monitoring/resources.rst create mode 100644 docs/monitoring/service.rst diff --git a/docs/images/CONCH_available.jpg b/docs/images/CONCH_available.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c1918a7ce31bacca6b43f7b849004f2e68701bd GIT binary patch literal 45528 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 literal 0 HcmV?d00001 diff --git a/docs/images/CONCH_message.jpg b/docs/images/CONCH_message.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ddb1ecf1446199e557efbdf86a144f0c0046dee GIT binary patch literal 14360 zcmb`t2{@GB`#=6zvWDzMOrcal5ek_kTaqaIGD)(Isq7i1lAS~$#FVuXim_`ZJ0Uw+ z$50f888n{E;{Wu1f41-U`u(r}=lWm2d*;mZdd{5Z%(>6p=RWs+pL^%)4hi^S7~*vc z0L;ySV*mi~096@1Aq;i^uMqR+mU~?VFv(q(SNs(@d7yi(H`pa zFEaVZ{ohxfCjh_=)o`9rQ#+Z$_BUo@=KxZp|H2~BcRerv+2-Vl6EE5R-TO=SfA-FX ze#!Aq4BRi^`bQh+;XkB*)xgvgx?Xj=>+bI7?}-WkfSoz^J*EZ*h$~hWMy8hx0XB#` zcEI)aZQnlwM*0QZwK6_`z|P*`0QV1o7mx)6p=u3RH~-uESFT)wF!0aoAN+5kjsBS@ z02osK(--0Qs{v`brZwQEWUU|j+3bI}`#&PSo9_N@&^HXI9C5pS7aAv<0)(#w2i*RH zp#q5Ag$4!T*gx3gZ~Wp9zVSDH|5ux9Rt8X;RtWEL^K`ul;b90LzwsaS9{+)nzJY&! z_YeIU{cd+Z>#KjRP}vV$0WJY*z$rijIQjq5?$lrH3;+~x8wdsN0$zXz;0Xi(2LOGj z<~HC9xI@?!@B`cc72r68)qoR!`usD$(6bQzd*1$C=XC)9H0PoJ#r@xPZdCwKCk6n* zRsXI#It{VO2LJ?-+pc$A|Lza^WTSEV-%?lm>z<9j1^{-Y@9b>YLc(+u0GMMtJM@~J z9cB>#a7+NeC)&;=@CXuvKg15bIoR3RIXF2W;^gA|LtNZke-ZaTi03cj{X_i!BDOzI zad7N{-uygVJpZcyuffhVB*%MqJ^{i!z+cc%lSyct zoV&QVd3gEw_W9e%-jOUqIdHDr}Ma8e*ysfCjRaMv2*3~z+w6?W( zbas6n92y=O9s4>yL7JQY_I+XT$Im6|`o`uj+7|eG8(PbMvjctp&C!4GBMkAw&dJHa z$^C~PHuhksa0qkmI(U*xMBkFz^^T~V+5;Z3^C_=BH1W!-TT#Ss+#TSPP&h?8ME%3k zUmX2+3_bk+#nHbQ`WHVt6Tof`Hs}xI5C-4?i*+O~9{B$%O4u>ov&eVv?#6hk96S^6 z`FQMn$o%B6tWaTq*cEQBO2)aN9U!28d4?jwrls&rxyfnnJxClFd z{8*qNaGUTy>jsz~=84;!_B+6=&knGDHu%~VZvOvO2m7Co{GYlRMtaR7eM21{4S2nW zHC8{u72FF=OZ()SU;>;;5wpXZ9?4|G@C_8EzNND=a2kC@Jt$n(6;=maH_lCMcW4(@Bd=8 zce>#Eo4RUo%NJ6t+dF^{3OvS5Ru(ZV^3FX)hfOs8SQtdhCKT-5?Rv4&d75{vxuUim zR1SXf6*!`}eA+GYL*}>Nw=3e!%)?bHEQ-Ta)xXu|C*BSf*F#^rx%nfl!H>M9RP~ir zgg?sg2el{$rrKj|MA4tQGBuNR5!1JyR6by~D*9vHImdEYeb}nW6P`6_3r1tN2)`HCc67B_!U$ zT^^g6R;l;;7O|CiUGZUSAHQYMd{H)XU5`eoAurOkLKKcT@b63qaGgC@wI+`PUUaVQ`Ci1ZkVccN?U^lJ5j`9Z7B_2rf1A|6W=9KD0UL?E5`K&iua70r%5#u<4}Qz0J&*jmlIll4|=7 zFfp%B!AuO%LMhnRayEOk*@uCJG+lkg7-ag~UQR8}ly53i7AXtrzm0EO9(*aZtTev^ zFeH{|RX@QeYT9;y58p7m^%TLyHd)S^ipJ4fl!ynina;!kEYJdNwVdqai|!d=W#z| zqWIeaw5MZO&4l+vh9n01>DIMa{Nb1j6j&^SV|E$Ni_dfm#E4J9$F(n24@?%mPQJCh>6%k;3ZA~$V+cQ7tuFArgE2#I%7xaU5mf1H)>OK@Rl^$s#% zlCkdsrrB4`!oJth1!gMR?~Vb+3MKnjZ=@J+6Q0>x&}|~R5dwNr*vYXL7WYT0AhtN7 zcPm$Dn6-bR?Bi=)2l2&Nk4Hpvp0H@5!YlUGr(tZJ2k&2JW5sFofE`ZQmI&#((bANg zse2l%>)oHLqArb5VDuXd0}3`V;sgUhMKHw1oT=6|ab*$WV6Y{~<6Ag~t1yF=cd(RwQXK$C)cNByCT>El25HzdpsW z!TTAh?)ST~ce1V*xObLFwnVwk^IOE!)MQOm>SV^Lz#W*}(`Bu|ey&{m z{SNRj5`B31mjJ@#J=G)ndqclmJ=XGppK&HqJUya+HP`Qa3+{K!UMK%}1_6wrNYiqu z+#Ti?w44FocY4#Wd?xPw$a0E`Al}LQxqPUxOQtXPKom-3bqS$j_yzrO@?uhB58{JX zE7N1PBe9qo*xNJlO}-0v`W z*OImdurUr_lFBEPTWz+4ih2&8z!3s8$B+e&&s|s3wwzU`Ria!ChpzWrX? zm#Vq&Ta$)Et21__Xr+)-(=*pj7VwFYy>50_p7c8dup!E~#w3E$)t$XX-bqm9Po=8+ zUNhXEI)wy+=f&7!UV)QMxr)Om97e#K<`6ZL&a(a{G1PH9y^lF=&3h!|$ESeOFtX#t z!Ee5vjfFpeVB5g|#D zO_;@UEoBKN`ue?F9AB8lpv%+wCSy5 zvV0##-W81=pBL%7{xsjIP1Okc-7=0Si>pJF@DjO;AgKaw#`OZtSKI+R;kHFnEHja7&eA>7tzax)+G*+s; zTgM?^sGM6~up}Am5FRzG-gjeJrsqz5W%J6b5huZGX$SA0o{bzNNNvJ6(N~#RybicL z7u=Mfv?k-7D^X3^iViea?cvw^;Kh9Em%fqO;hDw%vO-<oUa=qTQS9U?^}>A1|C{V(0ydfeF9JlSU{C?*5gK5!v z{}zt};W2~$y2Tk_U}n3T)2}l}53$a)vvLM72~JrPZqE&4qgW$?N(nSs{s81bCIc1-n#>UhsGX38=f)E=Oo&uCVpal*>>2kROAfj zyJ{_U-1~P?=bV4+FOrn^e(bY~h$D)!WEc_@t`h3ixi3{7B%+|_ZRu>L5z9Jyh9@Jj zQj=RD%5pdr;C!O=8+EkX2J!IKLvy$XPy4dR%_`$xvT&JSJ?2ifN>1y3eMVJZSC_Qn zj|TmIEA2IK>kBoco5~+uBm@V(T z1O~N_r}vntpue4a8@yjt1($0#T|W+6}{py+xK;T4n;Pg!@;7Cjtq>@yqU$y z*VH))?VP!qulboShf+@;K4OxMO8@*$Jg&SPK8q#7bP;rO*5?^%aA3aIr)8T~QWBRH zmb}~E#m?(>=Xdn$c|!1u=N~>yJt_N?#&-J%$IAARy!-O;M_wix@;$QbKl14fbia2X zKTYK&`ON-+v!@wHpH4Of@;grDbx9|xz3-I&dDZgV{w;=_o*3jFxfe5kTTt>@!Z1Ee zzuFLsQ4HD|7y>sYLr^W%{>r^MQ8+8b>6>xal3%e`zs|eA=f2V4>B9fgh51j%ZfEfC zb=uKbZG+2d4piOUbpL>8(sV5B)z8Ozgg96gVa0wPHlavbje<;{OSxa@%`S3Jt%fwb zPJKnqI`ZbF?z=vDw^a6~jYYQCgd3;rYPk4NWV=nXO|-m_ihaj5dPIz#DzBNSk=;FN zqr$tf6cA3yX7O}TMhC;rddxLpTeqs?MUGaDPcZZK2duQ1pO36aoQ&UuKZa8*WbRaz zc(L|&3TEZnY3A+cjxXFT5nFxw4ChmoRk1$VxkTF9 z|2Sgk*ds3;N0Saz19fTx8!anYODT`sWFGseTU_(#aMRMA7c_E5@ee&{r+Qcy@fjmV z@I`#Z+JRRo&dMlVGxxV%+T`2}%z@~kYue^h!OHIH*fbmW^I|pKjVdcDCQP+FoB|6? z@WI6F05x0Wlzj9F5OU=x`$OCu!GrTm)iwz{-w4CB*!<3%PROfNousFZ8Mx~7>2dQC z^OG}LCwZf6LHfKFLljF}X67}jQn%(=ZzwZD{9xp~MKWI2J1Bxnsw%2=)B*9x-mSKw zzRupJr}y@w&Vdejt?uN^4~Wq~F*ZgYi%6tcU?+(4#%+j6{#Fbh`hW&MEPe+NV5q+W z?;7GOBI(9ghdicFxx44=zIA$Aw`tE~@#KrFxClwliHsP%ePD3QL{EhI_vlP7ly0a% zg-M>ZYg*@N`ThIv*#;W|VwV4R3T9{Mf5#OXm*=+HRJqZD;MF;cMB<(@y{?y_#ija@ zU%U;4RWHnsKN3_6dh=;D0Zmh4v$Ny87-D*8_KYj z@5P^o=TNFv{jZ3LDlbyMK1;gCtDsl)%Yz{jIj5Bzag5bojXw#dy>ni!F9FNjZHiWD zHd$`Fu(2BaA@Wn>Iya(EC2<>C1JLT-jE-mBgw%C%get|BEI?T#1t;R=7}vm&cMP)z z`zn4=72BFXN{B-qPOH}Fsg5*D6_sLp@08}hzPqLyp$3N0o`MPGQz6wt4|afkjPSRt zK`f=Z(YOdiHNidWhXbTPDp`z8NJcMAgt!b!A2~&yx!HZ(2oQ^&5;x(d2^dBIfBdOR zL>x{PJfhVk^k|#A9T6WS#ocB@Ow*W9WNoB@_9<2G?P3;R zFqM0rdnFR`2nVQwuPA&Q*M9v-?pAnMRSupLo;S)Zk3)abQnv*8zUn?tyv2cr&p za{O|LXFQ=0$X--8lCM1N7N|jqY|TAO4Zgpr6Ykb#la(BQy|Hd+ez>TXuE1^eG1Um) zSZkt?=JvT`f7rE7!m-ih7|iS&?cjFA{t(e7SVehD@70dJ`86*4kfl*l8F{nT7gx7B zF^H+ceOpPN9zOaQelq9Fh_v+!Afnt7A;=op0jgE%=-Ox#Fr0$=G{a7y?f~%-hmxF6 zB`7JqKAz$}crZSAqo`Kybl&>(^G*x46K_{O7x!vFd)txG91?tdZJyWx7YWf`m_gmw z^0!;cy5V9h@7#wf9QVE~_w4Wp2l?RvlQTyfUI9Qv`T!z&<`A>N7vqkaaHHi=$J%lX zEJrq-k1diGGDshJW{#cLkh$AC;Cg?mGkvt=c!B91B{jLcQ^`5CbFqDnp~( z%Qj*!s~>z&=)KJq;|lGQ%bME5Ql)EgJ>A6#;YNyP0Zx-n;V*mSPaGGVw7(Sg34Z-Y z`4{ZIh)IM53uYf;@ySIAG!DXw>juAd`ME6=MgEn2rqmC)x$ItFhYvT1iXPqjrJcz0 zy!(;KbF~la%YERnTx=0TH}VPr^eQcXOlBQ)K&y|SWov>kKDg2H(%#-Wv!`;CW~fuQ z4x0wQ{Q6txV%kfg(R)h2nM@pfHV=yv-vL%)sHkQZKgA}0Rw!O}7gaS{_hgv?vc#c> zrs<+shuh=wb%t~NV6JrXA;GrP8Jj8N)#ct_klTHgR?bY>0el+>qBAX}Gw6dLhO*c^ zCaXiq&41H7r)+VlW(4&s_S@*I29I>JIA46Xv@>AW`FEUR?ji8i7%+ zswQEDFt^H+b763yvc_H1Y*NR=n^cXof~=_Yd>>}^y^KAMOQS>N^+iX=SjXO)ba7;- zq>s|A`|#U8$~)4T1rb_H#41b+Opsv)V&)LLLmtzcbB_%N$QfJh!at<~aDE^yCKOHenfOdB;n7}03g)-K}beSZ5 z`B-G}7WB78UTh&xB-N|XZ;XnV3Ey`q5~%ZX+=n=ILD0C1APfC`5nQ@aoRU9}iA3S| zIfW?AB|p%UCWfq;)UZb150j938tIC)p?mhGhwT&7t?nt?e_j5xH^8jSBZ#84=#GpF z1aOfeF;9C%S&U_{(RKjAXsRT6HHyD4bGFVPEB@&ddQGp9Cxqu>GsSu34P|l zL*F|KL=+7mp1`;@3#ScH>sJ;ZkUOfrc2(j`Iw%JViDk9v+nX2(|ECxs4d>?a7_(!nC zo;t6aCYP?5aDJ=3o2_M(NV;}EqI3X}4e^HYOO8iKvV?r}j(~S4kyje1HBo5maFWm- zFlgTPLsiY-S^zlebtsiN>6_jw+UgeK8hqH0OXEt9ZXjm1bY}lpqbLQx*d)t;i|Wz( z(V*4gqUZK#rANV}9TBv-*Suhql?82=%Rk`bUc_sggc+j{BMzJiP;J*MO zDLRk`4jQ}$^XDwu%6HE!IUuXbO5Aqa8XH#)xu-A9`6@_!aN-|ByPbUA%z5L-5f0kj zc!nnhLw>sh?5={xN}-}pS%`*ow9OmUB218~8%ASl;_BtjZm?zv>=GZY9G`L80h%OH-RoQ5@A-dD` zM*>RqNGk3_l?%q-N{5W%6tM8s8kLp?`DLji92>^Y(1hBty75vVma;Z)yHZL*2%ryA zE$%b)Ws8-!Vm76Hf`&J{uBT6InZMAdp*!yEF7`>CxfcIe`~z+SQ<1!4PQT9j%>%2ZnGvWOhrty$l}{MkQ+HpPK_9Ii%gg;NEhjfzFotxkzGbBT)bGRInkcy&`jfS8 zcgC4G%q;@wLmV$@#Z-3C#u)O%N%9R0POq0nP+YOSJ&k$0AvfL7HNMrZo_i<$7bc!4 zg%@COQ#z@|9WaR>GrI?}tnVXyGALf7?KYf`M85pUj^*Y!Gxt&OGp*06GRAVK(3HGr zn0nOhdgqUpW1o#Vg*)XFuW&CWLt*BxE%WAmWR5wD33ZwUn1iA$+EZr|-15LI8RPR( zTOM z!%)S}2Kdm%l6myj;8YCIrBsPZJkK>}a=_L2$FvyRI=QyT;`k(J_K5)N znE2JJi4YPn+8(l^b7~4HmyUmolHg695Iv>p*SG#Y-Y$U)@%8O@U7vXZ2SbBWy7d?@ z2iZSdafQX1p|8EQgeN8@i_T3eRHR+lcfdV%e>}jL0Y5@A&^Z{P9MV09NiIXXY)6#L zhZ{F-FU08HyJyCXc?)W)RS&6|${UC7@40jQcuM5Hn_o-<3G?u;7Lc1mh>^bx7E(nG zs;JR1b29R6vf?AU>Lk}fJ1_0%#|Sqw4`t?5jgPjOf`9(>fv?|;zhkoqmj?zfh#2Wx zsqy@b4+J7^G4z=U1TT2JWG)}rHHV6yu&!zAi^=-=i7{6!4zVJij z=&!a$wZn>PnPyMsTrrj1>tm>loZbfh&F^AtU(3$26t``eI9g@`ED&H_ZVaE`BLa%qcZ&v z+PV?!pd`$9v}5=G++K^-+v_=VXkbywC3f4rIuCz9Q%A;ftt^pi+V`A%z(cFwvMwjk z86?P=Rh7n@P|B;(G5<;?lh|lZqlv~D_&fO#J8WK zy24L41?6e}c;@4GdRj~@X#^Xs0Xgn>9c8#zX5`;j<#N?js4pS>JQp;D(}bTZBb{ji zUxW}!6fK7E#-+gpYGGe|7387+OaY0c$EB3dtKCfz;ub>oh4|OkvR5>|Po+*JV?$Go zgwoo3i!o%R7~Ais#jH;*n$(DV%2R9RD+a1KK~^A27PJT@I3&ugcchV+$bZp{7=xg8e>$*Udsxtr9xTJA0st08T=R(h%B|$Z%w=addF}t8htOkL!hna=vjp&0>vcmIOVv9*gv@7!! zGpCYnyfP-~&%}PF>#u8tm!LB7mwdl`xvf_O#mylzpn07662Au>#2RPyL4tnuwfEKz z;7ZrZhHO@{vnCSmk-4Dw^!x9bTS2A`ep4#JLa$%H6zYJ*!YCI@gIn=ON6@wf;FvKX z#u`j23E4(+w8_dK-CDL_6Ip>}x$m9rN|`kihblE^!f^|Re5=N3FElKQFionBUL@GK z7s1_vKcuEvs`>%uT}ZO^a4F;J?DJn{`veI=Tb9h+L7DS|I$p;c$4ug%?&D@UknYfh z3;}dYKC631dJsCOGQ)huFWTVog9j;|6t|_|EGy)# zt4f+4(K5RDK4B|tc@Z81XwB>mo90j> ze7a%t77P(+qiRKPGj4zfXJHAqK{pbi{p^j!*5HYoxw4Mri+$HVuIJ7g+#5?VrkNJU zUZiBBPWic;K0S~;e(zt$B^)~=|I18VfK3|El7}!iQR8kMFr3XRKa@M=OQyo8=EcIS z4a;Y-zNOZ-3(9xyQqRBiPcJVn`K+gm-UA}2U%Cy3B98sUPK?g%AAN^5aOKUi%QrVR z$EZ!%+U0QODYnFZ7m%HOcP-5c!02EoX60g_0qrugq*0Zk+1q%93Z3%D&xFKpwMIyd z;>56XdT9&O#9q;*Y8x*_S`=~vJQJ;CR9lR zcZXWDbm9shkOOsOn)ju>3Q*)*eV2I2FVS-3S&EZT2WlKPiy+ICdd?gJGh8vUHKcF3 zGCDQ`fj&%rtB3p_@o2d<^V;ecEh=q9VSi~Px8Jz~92?861W7nDIRP%Mr-T*-ZK;aq zDb{_Tk~q$#uCIpgQn^9pDt~K#cIBzX{xSHEdl6q-J)d;m6j%^F$s^I2JiAB^rMt6c zFne^BNW_OMRyrYnV>T)eCEO#l6>TN>M< zR3+#@n!Xopk37Rbkjx$-wG1O$GKZ>?%r0r&Gc4aO(HZgxI%t4jLfi4!cuOBbY z%wh3^u(^w92TD++U}jkxsauuTP`Og~c3a-hif)ancR{2_JSn<;xOcAagBCrn0PtKm zJ6JQn$Pg2yr7=_WbezGmj^_HJI?B^l7sco)&;BLNtccS6+vZhM{l+@Uzh+RY-6H+3 zj}@#QZYbxDAVLS3<*nFAETuNDG6`yr;lB0REBCA! zPt0`prhAB>!aN`U4(@EY{f_MbD~#0-r(8q|*-#}P7&i$ChS=Isf*+m=w=f%GoSe%p zy(l)cs^QoadMDfm<2&S(dC2v{+*740TQ|i6_k-Y-iew?Ws|9s&Hn|Bg@z~7ltWP_F zFOVP+l5sh@S2+n^ul~4~5 zf_RyT{_W^Db8Dh#9o7(1r&UuM|E7TGP*kekt?@$3W~(H`H&tDx!ce0%`&IPW-}1>A z5JBGqh`O0-?GE@p^k&eq0!$Ti&bFL^3$CV?(1&3I(8${3tJ9(9sHKKPkBze|05eMbKXlp%WNrMI* z^?^~N39`lh%A9UV*}u*a`mG z;^YWz1{?FG-mxEa$Z8C+b?(=74{@Ov`uH?!Zp9#q&ww>U8(lkx1kW4W@mEl z>iY5@CZTgNqELv|TucjDu@yFvdw>$0tS93or9zm)@SEj1^%jQ=(s&L%-nxjXs9h)A z6Yf9Px6exReQ!h`!-g4;xrI%-*B6I2rAoK-1-}WY9x|#JMjgaARAD`0%5F9eWpO|B z?8Q~rh%|oa<<3x z9*<_|sz+UDL0IT*qs4>6$5Hhf)&MM^{hg+kDqGqu*l95V={xV0YV_@X&c_ z0gv|FeH5)0vZEsaJG_mfZ9P0D_h2^Q!>cP1xv+7gJb4c=%~BK&9w+Ylumo@JlTybo^~=|B@P&M z9T8qNeVBGwt@k%aI8}#M#LQxd(h;mt!Y+nuvE2`>5dH+hThe7;{h-Yv-Pxww*zx)I zMzxqI2MKoAA>ov!<6rh3!{{S82)Fv8u$4X9&%q!KuT_ooxN?+6uaD(e`5}qfcNhIM zl(I7)8tr{_j%0M1TcaD2rw=hJBXf|}+rtuB#!GK7;8e$}*>+U(@=28p@>Y#-lUORs z{?U`~%CDqdJ%^FA?3d5M)d@cE$%MJFroP7mAx}xRt-^B{A>And9TC(P@NgOR zZG^otB@Ku3I}IY`Qhm-G|5bbQu4->1#;f)r=^5!}(m`p1KIMF*G%hjntQT z2OU!NVR)5c;1w8#ac90NU9zQVO5S@CSfBN!ePO{&$NsRr%)XaVf@AJ`cYnH%mZ3X> zh`F&u=sa!*;BJIdeA=#rs9(z_l_#NlZJf_)9F@C|_>sA%MZUnr6^T9dVE8bpQbGJT zcQck^xi*QIYa>EgnFN7?RIuW#MMsc}0@WgFGux~#P#Uw#`}WV2-eL!DNlS+gTE4dF z8IO$aCgCFKZ_EmCjlPf7hruN$oGOQ*tq0e>jp20odsQzFZ+>*UGO(tM4CIO{6FGyU zX9pv+IulQRxyYXRwWs*m2xbb_(<{?w{1+u^^-$E+PaSR)I?a%Y zWl4e=EqZDczl2i`HIGhxG&3GtQ_?wZGqs>szhikxj7+&AMj``9QQx&H0;ClMa* z3`nJF9tUFeM6cddPT-A^udt<3-w@mBE~V&jqY?^FO~>(u@yusQzb7``w|)@&k|!8@>;Qh7JHKU zX3RbPjt6w^CVEKQ997AAmA-c)d78`Wbn<;{4U8N|zy1Tut|t&;;qsoM7{_oe0{d*J zwy}`@7`1m0;__b3=OG_2!*<8}blJN0Y74`m2WOZ`tBu?wix{ck zD31Z$8VABVEBe9Vq!Mp!>(v~Uno}8)%L3nXdbG?0dQxjLfsD^eRYAKh@UemZ6h+!& z=4+Oyofouc5u4wrsG^PM`w|d)QB}k}o>|tVZC{gHw!a*v3nVdzgF465os}YJ_KnZe z-dDh$M=&GW_0*YpP?XrtIbzsBEK4zNB~WQuAv7;~!u)(QuL-U(EG%@@ttQno==X=? z?@A6JYtO&DD*%moo|!}fQN@g-{S1Ze!)f`;TDb>@6b3bv$eQ%a!{y!5ab#7bpL~7w zM)3Qa@z>55`%O364q|y&!Xcuw!4FFDGK@11=LFQozdsAFSf9UAmuf%NrI0!`-F0fc lp+UsObYF5QaL-xH^4zZfD6pN;|LF|>XHL@pExa@S{{Tto_yzy~ literal 0 HcmV?d00001 diff --git a/docs/images/CONCH_slack.jpg b/docs/images/CONCH_slack.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d08f7ebe4430995bcdfc8635dfdc79c3978fe8f GIT binary patch literal 56494 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 VtafKb(;NWt^F{|Br}yV1cti{?IC~H~0l3w0 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 Oq>ZDADov)_6=i{zvSy*COt2y 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 W|7diBZTt#k`$vZ5|F&~B`u;!hKft^I literal 0 HcmV?d00001 diff --git a/docs/images/MonitorMatch.jpg b/docs/images/MonitorMatch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..049c79c28e908b28f9367fda55847c2d5dd3fd60 GIT binary patch literal 52851 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

  • fGxp2Qw&5)eWijBDsmkVy>^MoEOo@a9J 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 U(aPBVT3`P6cIy9)=WUPuAG`heU;qFB literal 0 HcmV?d00001 diff --git a/docs/images/MonitorTrigger.jpg b/docs/images/MonitorTrigger.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6b1687fbbd18e15ada244203d53c48aa0e1ddb29 GIT binary patch literal 37170 zcmeFa3p|wH+BbeX3nk|$h7b`_M97ew((3!%mBbF@YnJ& zo&f8g%R_bk7EJzp|EDYGJpkZ=}&1UW)|Q<^lxc?=(CSmzn4)xd^n5g zueE0}|6V(*UKY#mX{Ll1?0+r;J^VB1TN#*|LRTx->uzqo0UmyV0Kk}J-fe1NfINE+ zVPtyR5MY9O#|*e!yLRlz!H=ZT9M7#^LG=m%VqL=aH zCzA}6J{uBv?PnT_fZ%ngQBeBM&$RoW(ocV;Fa0U~`nNLY&lx~vnxXV=R}YuVP`VdN zAG-7(^49AD^JDExKY{+j1`3IL8x zLGQ)wuX(PO0Pt220Qf5Zns;y%>LwQe;Q6n)TzC0Xeb8SfGHbwL0O8{UQ0)Tg9#xM{GO@p6-8Tw^mW@ct#Wq|@KJL}KD&cXg$ z;P`Xk{4MPG8Myxvn0`LR!omjqa&xkC{w4pH6O2)49-m^o1Nb&WqfadHJsj z%E~J$tE%6;t!Zp(ZfR|6|IqQZr?;Alq*U4Y^({GE`~~(OiQp7GdH;Fo`yJ9p z&k=<#UGL@+mQf?fl74pSw;ugkhi?5_J^D+B{?Z@D5WvI21idg8J^&8TXXLZvfPWq< zvb#49rUhPm8a9E|0zPGzjCc-I-3}zBNYvxaa`o5*E&shJlq;O{>U3>Rcq)@YtNL4-du^v`o1+S${3{sx&Q&z$n_HdfHe z>8siilamE^AD|$58zi*48ISi-84P&0x%AR|Ott)|QE=JNp|mF^ixxE|OcKuu%RqmM zDS8K39jL|tV#egk@HH+eRUBjyC3?~7QhyMW`hHh%nm_{-MzKD+z<%`~$_-MfW8 z?FIUsJt^9Bj_Phhq5JDhhJt7RS>#}R`IVPhWS9LRM9`x9nel=i>?pmT0aV7^#=7ES z&Vn)LB7)!oUX*0Bo_HUg^8qrE;oo|sgOAHZ?fITGD>A9FM58!S&H82ff_V4N+n-bI zaF`P~mdWEv$vSC$2exz+c7u!r6*b~3vAgMm>7S!8i$3dn6}h#o;ej&uWScIA)fI2q zh%*4z6|j(ikH$*`I%)as9Gc%=xfAqE(W10h`GZ?e@yw ^*X|pZ1rrVkh1&n!rVp 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

    ~!|hPg9HQS09u%L~5y| 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 literal 0 HcmV?d00001 diff --git a/docs/images/MonitorZendesk.jpg b/docs/images/MonitorZendesk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..796695b615526cd78f0669d94cf97e30d89dae64 GIT binary patch literal 77867 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 literal 0 HcmV?d00001 diff --git a/docs/images/MonitoredBuffered.jpg b/docs/images/MonitoredBuffered.jpg new file mode 100644 index 0000000000000000000000000000000000000000..db53d5173c5fc8775d81f97b476eb28b3aa2884d GIT binary patch literal 46178 zcmeFa2|Scl%WRzdV$}|$9e#azmx~- z{OL0K<@3*X*1G_}0_HFtR8>8cLiaOGN6!GH#{5L{gTH&5`)ip)2M^}b{a$-6{jaq% zp2%hRm8N@L$oxwg@Zs;5-Z_0^WAJ{?#n;u<$Il(^4*;}jdR}9FeVDnW*(u{Q1^^w{ zJ9^;awQFAA8|>}l?`vszVz;fG{ce_dfE}!l8xRH*FS__$(=#_e^HZ(A+`rSmUz@|< z`2+w1O5f{Z|8{=QeRZrm-f_)0mP>iy_j3P`V!!O_=K}tQ3?_M(YrbGR>109LJjnmr zcN$E9pfA`ckiPw$cKexr^qs!+GyVLhGUqMz!7}Y2&FkWR@iIscg7kq)f0OU_H?+4` z!1wR{etmB}r>l?Ex$k!{NdV@+89)_K1Jr>-|BrHNKb6x5;J`H?81Mx=05`xL@CSAS zdSK2qzzc8%X=A_#Z~>Ho10bym9Q>19&kqGk(9AS(tyiSbn)!f4bPdUmU+( zbl;z1U|<6ObFebA{+9oj3EDU)#|E@l03R#eYsM1{bkYDl9~}c99j%=%AM6v;&n^75 zlVCd;nV4Bv+1NRF0eU(H26{#YCMHHkuw?l6VZ_MC#J}f|9pd~ zyl4@!?8D0*zT_Ll#x5+fOH^#{J~?@X{c7qOnn#Xm>7O(>WoTr4`rLUdYa5WwE|*=e zxVd{=_45x13_=8l+`JVX6MOqkTx#0A^o;ut9%kkj6c#-xE-8IlSyf$Ai>|A0Xl-ln z=&1jVQzZ!+H++XjEIOoLXH6e*FJ!e>IH%3Gu7Fy^Y&uc$)sWc6DewCoo=5n5 z&!hcZpJMa6I|6oz*KU1$RrVxAq2;r6eW0g=iT|vWopR2|=MbtG@n!6E$T!tbRxGid zC#W+)rb?0(X=imV4UL$Lj(u{^kNeg;N*^bm`|nD{=-TW>J+6kY>VW7Gp=}yK1DLoe z&()Jh2ik^_Y!M=FmB{DHTQ=k4TcU;o3-2+pct}JE(g0RV!@WjhJhAB>Pp^I2et%&4 z5fArlfS*?j_K}v-ScrT_5d1Sb(1?WUkWwbPO^FImPK(4O8`~mQ z_SheqRT?h4E*8;yu=8_j5=^s4>`o62*qau0E3<0roP&Be&yqrVO6~I&$x_8m8gQjI zIn!)K4{zN&9bKsCQ94#%HKcTR)}=myb7i&WhPC4ci)7iTVoT{+^R!zTCDg)hzcb3N zHw%(m%reuBN)5K5Jp)f#3Bj=mX2sh|PwPWn^L zcf9diuliYQRxDLCLQ|rKKS-2}QO9^sjyj>*h#iw>Ttt!wRs zt+MuJjt%mPM{eKKYTDAdWcIZ>6uweM1LV93 zBGk4m%+A4)g-J5MO1rKs{(cKGAwvH7K=IJbKwfoS<%>uSQdSAB-i6=W=2#A&3cJzk zsSVT{7!UE>3~a@P;QM*r`W!L79ZTtPbTSq+5rf~GElj&$n}6mTbblNb{1(^A(Mdzf zy$I&fFomjzD(WV&nhoXsoCoe(0^;!SI3bI}WW`iE^`TUY^fNB)d%r4Db$AN#u-#o( z$PTdE*Pfoo69O``Or}eUc6BJ7yFNGPdA_`2kp@U0+x#Q?AREWE4YohgfHfuvejHM5 zNa5w-iwJ^}Z@`KLZ6?Dn#geySp)}y|jswMV8o49Ch{WlN(}47x2Q=W14;Yd)4OjSR zz)6O7*yCOru-j&#L-4l>4$^@0bve{HtsJ6R3=Nodn}hzjyaf&5>LZ;*_5lQuFMc%O zwb0IW1Bh)N4bZijpaE~8L&|bAAU~;?DuBam+ifTRxxCLnp{8J8Ce2asu&xWs(+kW)^$GiVU^0Tu6N)=TLk=zv`7h8k5 zfu*LqB9Y#3o#h;N1#iL1{ngL7QWK;fw+ma`7_^zQAq%dP&QZnX+L2h%wTRwgN|uf` zs_l3x$_c z3y3#%`wB|EZ4^1R>(=&pt8$7Xd8^|+eJ_D5V;wu1Qh@Zv+}Y%He%35c0B#(} zM|kIZnyq>RkjDK~bR}Xfgv~DQC)m18I2y*obbQTx zVhfvRC8Bfxh$!kZ>TcI?qW#*VKxzv02Ew7O?oo2|HBo0x;`4Z4r$cL4c8A)h>;w})gsL?$+WN;=<94YHNE(Ok&94zgV8_JF} zU}lo+lT5VL-A4oRe812DDKINwal@SDpEXC2?nh&O5{18eQG#?|G9gJdr1Gopv0skY z#T?z2X!m}3e+H2A%_SH07BUvUGKudO>&33mZfj(pLVn4)`xrXqP`1e^A(1o$3dBkf zN#`m=Ow9y-TbDV(1m$r(`0yAfb6w=+NX$y<8;eg80f0Hn7rDp^)E>OOZJ3GHTPRO) zv+}#5E|GT;f^->ofonJObYhqSX+Z6HtzNQ{iK(H5`$Yfx>FWDu73z#PwGWzU2ECiR zEIEogfQNQM;*m^=b2o5VqiUa1&wR4`bRu6jxCO4PmxUCxL; zl&5Cs`DWm}-mCY3(T50jd4{jaM7g2&1+G&$lH=Q^YJ415V$`V$Crs+NdE`DzDlV-4 zS-dd#n>gpkcolHB9qC=@5WkSMBC;<3D)<5C4CO9hwJAEI{^^GR{;wID|1G28J*=v$ zwO7gm!*ZDb?#;^fHV-$M`EAn4XUZpLX9-MS((Zy~TRYW>wk9OlWJHW*iw;-Auq@U? z-{emQ?mTVNDf6+O6g!!c7HRCLni9n_3p-F9QXE@jUe&pY&hTZZJc*s3_v?PXGH(zf ztAP~6xFc_;I<|3UzOOG;4epoq6ia-{dw7}scHViv$RoKt&B&?8FfVYs2wWyRg$Un#DGHLG3SQER|9pA4*OFC&g7V_%_bHN zre})U#h0uFR+#LYi8}ZsQl}wKvF8HuB@W!u-?3k9S=rqaFb(Bd2+Vx>F8|2~@6}BI zn_h{x4!nc!?u>BcoDu`R?N|Ja;*S`>PR5|9;t1uXSJ;OHWV`K`NA6Fiht59aO1S=V z?33ACu;H6^i{rQJ1CI!;Qt1dPx0`J8O-hLc$LsOq?LqggmplUws5Z7SmP`0CFf>Fz z6f`P07I|d17_d^3-!wM8+L6o@#zhP+39}@|9vgGtfu(QWkx49aT`x2`$DW(E-jJka z&Qjv=sn=t7#&~VYcN3lfzG_;q%#Q$R)zZc62ZUCaado?S%npe?$9%Whk`Ew>`k3d+ z$VzBc#3%(l*R~0-4sO)#nO$9PdY98QUezKKHW0)iF>e>n;1 zynMTv%H}=hA;c7M#TMCOP8B!CSrPM4VaEu=DN}mEN{^cN*FCSke>zg{+AvBDbLC|w zZjYVC4L@c;VmcCP*kpm@iHXoj@zLFbIA=8CdYt~{x59IGSt$Oe%Dve~+M0O>FqPU| zg?8>4#X0-C8Ka@A_pD(351v)zeTZV6N_v12gNpfIC+R1TjxGCzHxd1b$t!j+xi=!x zV?A9H<8@!29I|@5;yLfz#t@U}b~{o2>G$8S*7Ukxar1ubYr0qP{A0bpXs@6%5F#XZ zOpYBpOLQRQ!*1BiwM~i;$84+n$^*uV@v}9L@MBn~ciMbkNd~h`(P#%ucM5t}ry0Po zyr|f&OHciP^zs+Ek_9Xej%vAo)lO}_a*H!J;Z}n`RjJ>r#<1_}fcbOJI(|RTAl^OuViCHI zPj^KJv;k+i03?*E+aIwDoXV;oo!|=Axl^4nDM?nJM)I~jnNXTZG(0otrIM{9Vpbi0 zjne+1sKoYUsE_c8cHQNXfov(FF}~m|4Twr18xmsba~C#s71MB=iO=kz^zd58+ES+F z=V?9sH(UZGBT)mA=ceRdjr&dS#dNHAwq#0viC5Ct7JG{gvP9+({Sz8if4P7r3(m&Ii`oMyd zd!-TuWotrC!#`HFPpE%TQr9`Ljw>gxJ38*$3RW9)Qd+Uiv9&i;n7185F+E9~G3CG^ zTTux%a7Y`9pL~XxBK$(Is^(?Q%1UiP*iOLX_Q;A$f$O4|4hS12+Hmz_{Ht?fpj_1g z`I0YF8#eE+)Q!lN89RL}Qg#D!1rY zJF(E)5xec@&57f$<lv`|9R(3%prklX&hTta5cl zA79muJ#~XSdWwe^0k`b0PUNO~I~e!LWy%Y$FAwwzG98Dhb(itWp6ORUqE3-8A!(Bt za&UKvikeYs!g${`wZp{@V1F1I0}X0>5Z~D~Ire@e);VlI)~bsvtfl{)tCJ zAs==EYj3N^DZ+a)+urbi`wb_4Xo}ShIs}6t9q}8<+7TAUM;J-Ur#$G|JgEhFHCoso zleW%bgsBk^8^-x+c8La^ICjIi6m%=lk?DwgNH$?Y12n*;r|y>2p{ik?+xah=LtlTB zl;RLLAhKl_%wUAR#Ap)5pqcrmQNm^c)WvoA$O?EFdrXw};lla3?kZH&jZWdi@F#Sfk%m$l zc?P@Pj#7?;;vWfGK?QJflyoyTAEhL*u*zuCUiAha!9vNKFuWSjDi~M3SCsE zXv_LR%#kBrk+~r}D@YuQr;BiGT4Y6NCQCEPsnti z(|*f?71+vHd;P^-`Jm!vD?A!B-70JAJVTtCX=`v<(3MUNu5s8`eS2)uIH3LAb*b^x z>Vnn#jOdWrFc?XT+B?Y`W>ZXD?aF4xQ)|i>_6T=VNv2>R0NVDwxuIgG9ewtB`hy|n zEOR!|O-dvv(S*t_LlBtBD9Sg4myD5*_Jy5uIdn_8@SVaC|C)Qj5v9W?N+lw>>sW-= zR$qxH9&VkiVOd@xFyOjkHsH+IuruCgjVE{nKP_eN9^TllE{o6IdFJhN_k~@CoXVHC zmga)jA0dx7VA^1@&btXQ4+$L3AJmq{kp^}GW`o5waV9+m>2?zOhZPWQimJ~oRoPf> zI;z~nTrnHP;O3rd8%#lBsSw?xJ^UEm64@<6@#i@MbC z-cxwPisIfr8<4=AqtZZJX9!=oNO z4T*%&lO1NpRN}JtQ;i{%j}n{4+NWLj-y$kY!fL(JyH$2g`Byk}L913@!S77S zJtv^sbC}$9cMtoQV_cdC{g*<^vchA1LGC7W3qDHVyZL(VWytUQlVA5K|L9J2Q8NM^ zpp(R)$=D631mdRtKm-#pryZLVJ8ic?(Son695WhJkFRr0|GKx{6Mo>m^-)RhmN*5? z3(H5TkCtwkMPoP%@CQJHT>*YBqgk{e$^A(`)1wouMqll+T|L8_E$l=eJ`!pZmi?B? z^B&5D_|gL7)`dn4hQRLBn0QNbNFLV1L@C}msdwPM;aXEWr{QZUjkD0(F8$5?czAq- zEKz@I?&b_UI#Cf(U`DyO^0j(fMtsiU_FJ#Y-7TLM61xxjzT&i56^TQ-B}eKW8S&2% zL=3#zVMdj@A9|+cJv5gE*B-tuH*Vzgq53MO_33fRvNAlVfSbii@z+7tYAimp@&h|d*!GrP)gse>?q8kb!Ff-XYDU4u;(-GlD1n#u3 z0HY1K2y7`NHqP6$1W{qn@mP0UHdAz1Gc<}6b@)jz2dqKpvo1i%X=WhH6F@1C*A*cu zp1@C(KIKB+&vbqa_t2g>+Mdj>c<}Xmw$5Ob^Is1yz-TFHTi|loLDRZvm}l^G_^A=o zTVz%I0#CsH6Ln4|R&p7;uW9Yr%)dn3Z#Nq)*t|}BIW4b!k>G{{-IZjEGKUix_=!cV zRryN$wa&MT@v?nYOx9N}U?g;9iSc%L)QTNRpZd-|3bZfllwxkFsSU3b zA(Y%gMI+voJLw0A9c5!GoR+J*y$wkF;bpOK7%^SrOrJ~b?!BTWMw>o{*O zC$l>io>|N)2-WCho!N3Q!b78)pk;4R^IAIT>Ez+sO@7C*u`Y>nPmjjew;D=ZBpfDt z&a9Yrxr?gxaw0fhO|aof2(y+B}xjT$QzCV{(vT zB-l<$HM1|lgd*6-!?=mz<$6=^G|$y)SWT`J#P!BT zaP~()h`OU*8;`lWxpce7b(gI-U&|!2R6f1oXxIT8B~6)i8F_tf9&mM9`as7crDs(x|d@?>yh(bBbTB6#ct6Iic-uThsf7q zeY;3E*3q@{4BKr4?GRKXRUl#{hiSt>NuV|JN*XSi+qokjVPlCQZvBy+%n7nXk3*EIYMA#|!Y66#6=u!ql95)XMQ-xo=yN)kr{@FVc@R2sk$f{rH>x=$Cj z@ER4+wI;rrGH03QX*G+QU?lFDLXEP_e2Ki6$s>ZO3>P^$5+JI)H^cOF&$$oE#i$j8aC+0v@Ohc%(#37BPff<^9!89`Ht?+QJu{htbCZpb;;i3z1QvCbFv)f#%f%Sc z>R>fO@vd59<|U(Y#w?BWl;hyOAKBVK45vKIo5y^#5Bf+}C72YnY}%5`bylX`FL}C- z-9B^u@Tr)CjppgQScKl7mnnEj>Rz6N-l=j{;>!?Edxx&^FrP5!MaJcEMQXR zf=^(N1ZTEl#9v$D!DSz+i}7(FNvceXEPq3mno2hDg_+!HN_~FPf|C3+Kn}2BA{2B@ zND#|N*U5YPBA6%a5%Dm*Bs^<2b@9xq;yWo9xJL=ipuV-$ zj=2S4YgTi=OLUtNiQY&S_NZNte^9QTB@fR{3b59b?$XkVWq+F&`JPRZ%M4G#*|dTd z=LX{sx9qQ08VS&}Ya|4IizVN?tiS#bs}TN{Wyt zXvXdtQZb!*Y*VQzT0S0Ixs!Kff?WA|fn z_!Q=j?t#H-NxO=@>;7{C-`=nr0Ng^-VVVyjFK#{B>{^vd!wXFL+#qm&nf@9*^^Uuz zBPgqha)nEmb@lV)Ay`PO^C2)|(IL$J13BOc(afS6pOI6{2CaOqr`;ltX zccHWiNuI+pNkFs;eI zlRv)dH)j4|;199*F|NK(9Y1D^pJ(3x>C>&;D2fJr!6HeUlQ_el=!u``;~WO3I&B{q zs;WXxSr&gQj^a5D*#FJq4y6tMym%=*yQVP27z>F8)al>(2~}N_P2rvCyDhZb*Clc2 zc0QfeDL@iHao46%_hsCqW_5ruSnOM;|Ifm5+6h8U=jI2hZ$7yDy7)uMyWlWix1x|I z&DV1$a!i7u8v@S9Jog>X<%;xGxT>}gm-^cV9ZDPdnc@2jG5*u#Lq{WK0oSow_4q?H zz>F6R+`ZHsiOOD~0iO?nnilNmoLjl{^@;lCLtlp8-^t%~$vw}mD8p4*@Tlu?KD)z@ z6Uq9!DzfW6&9)^2cy5cV$)0veDp*Y4FJF$N~&UMe&kVjC1QjcB@87p(*Sac;i$GJ zsGD#=$P7rTQN@!uV7B?q3~YzhmIh3Lp%rTes){S2rh_Y90qCe{#ZLE`R938X<^KCE zmaBKI93CaO?5e1&hw~y|JLrQkmLoKv-(#MkG(S!r_+OqF#M{FVAvA#cau(#VDWWL; zGN(-qB8_S=Mgw*>?}J=bpSG4{0{lTaIGvfBTx0f|e;D#B)@%PGjBkb2cZqQUpL%_% zhuXXctgR!Q4W{#_jN?qc!#QhOi;!ZP0-!%(j~*QidjcU`TnoON95S(`~A3BlyT zu29nad*gG2Hc*N7u*5KvDGaZNmQ`oG#13PIyKGf``o4hcK^}hO>v>FpGeXOk5e*10 zv+9!N`pX$R8qaD+z8*bPU1XVaz{YBfx{C;(g0H9(1aMu;Aq^E}(~p&%0>?VVVs5>g zSz4HG?3?1(em?39IpF9#*}<0Jl}x!1-XkL&g$)7Yjgz~ar74eD+9?7j2TZ>uCsvTf zpLw!Lu5`v;$Z+qM)GJ6*soCb%t{bx5ZG(LHN`fK+Aq({2PCdkx@9%AGmL^E0O)JT? zi$uto33LQ;?o?wRZ|s|xw#%Gm^ZsPaETj0DiM_yGa!Xs2#6$FZHNlDb3B4hO=rU*? z)Hc38T^bM?eqvu^=u+MAQERJIW^^_)R=hh~6!5XxxT5<&u2{r>Uvq-Yo?;Y;EQ=qY+r;)p0uv%`NaQeU}u$O9Wz zG2pOrs7$oL3B@@-CvaEyV#CX{&1=L3l69CX8nmr?BU^U%V%k&rgo*j26ZpDE?z$2$n+^Ln`m`!9 z3hgm2vh9mIf#!cJ4e_U^lc;-UZMWYtaqm-8#bWZ1WI;t%TaV$BMf?7uJC5}c z`!g-}%}IXb>qaCEaP9}k>b|DaZMnJJQMyAqdXTHxf3uSO0#*9YO$$n$_nA>M;&ncs zA6ttbv&L0LxjXd^LG2^=+7}J1kG=?Y%hH7`o|6K++hu6c2`Wrdh zOQAI2#t4+`oBYR*ky@F5t)dEtcF3CW2O6;SjRtIFhCp#V{m3unt5g;+1f01H= zeYWPzAFG%(mc)IiYgOk+E_Y?zEas2p{`45I=w9JK<)z3`LqLelQ~>C>mMHks=Unb5 zKdSk`n?H!#54ri9vG-$4{XB8}m?(ZsqTN3R!@qnmd~Q+^cW_*6K%^Q=*EXI{ewR|@ zdXCXF@x5`UZFbG@2WFK!(9cewu8Q(Nf*SVTiyv%iiT^-_;@d1;WF?*7`1LpdI@&L| zga6!4>CXyO-@o&~fnS04|3mL|JqRTA1ojIhqL`Yd=VaN$^1n{A7LHi#uGjKVl_$JSqv?t>w=xI|{<+=pM0BP_oL z1OKbv5K;|-H;0amoFxX35ILeB$>*!^!^xy$(Aczd__RfWeb2NtL3S+QLhTg;O*gXv zXVFXB;+KnRaaG45{S={XI+Fc&$ZGRGGCL&?gFEkTjxcG<6!Wl;C@5Z}YBV$sY=9y0 z?}BCH_ZDRoaGJSY#qZxYS9fJ^k)t76$vh0~chqgN715JqHxb6>0A(XOoURNPRhuIu zjt^%}$T=DCedT@_A*~@lJe!~>{!q%;JYn~KcZN-|I5IC01UWLL4pGIyP?Q@UQxus~ zgW8Ajn09lbpGE&HDJ)xhs0b#r;`>qQWg|BCW^cQP;+%{4<@Q6cQ`C150T5C%DFn9O zL|2EbMaXzjIDuejvz`#o=zx>A^6?R@?m%zIxZiA>6p9$28^4Tyx^|tdxEe|OoqnF~ zw#aY+qqk=~_C?t7(yh0RL{-%z1Fa->%4ZK#Av@bO=1?Fcc9gms z**hCcr3cmUu6Yu)ooAc;9JC7BL5Y-WFdi@h5aec#;A)djaTs*OWpI{oHaVbTA?ipG zy(fog9=gug4oJ+;oR-HbXuRH)f}Ztma!bH?p+>h}eUQzpC@7gQQN{$|5G($VMJ$JW zJ`Z{2hCZA!301Uv0$1KO{x&W~=V`=MV2h`U3Su{TAwz4`?b#2!2>VeG^k9XI-ny=7r6S|ss zd(wAjg{-a=<5k+Is>s*@!|C|(OB^POWAWaHTeZICdtVw9tifQpif)xLRF=N=z31P; zSr!LuP5#^M_`0<@L&;L>g*cJ52ERF-9$qyYmcdirEn}rhrBZ~84<)+PdRvq;XUhg= zs9S6%*$o-WvVR-^!&pH!HgvU*_1+5MC&rr(Rj-)4kNA!fVfJ+c-mBMcb?qyVjJM4- zUv$D=yUp|VcrK6EA72K#qIY{m(evr=~Kxc#>Y06;+tEA=d;)kW1UhjcoR% zKX_jlGVq|QC%UG0hhj$A7uAqC+XVe?sP&^_&VegWZpD+xLt&sbP#W}6Q9?~X2T~?M zkvr-o7>T~7JV^sasxdokDE{a3@(ka-80O$s@3&ZHo?k|}iGPAB)F|@MsVFc0*fp#2sLIZGGq^CBO|2H{8hpGb zY*O7HBLIM|zPs8X!QN>C~Vy4xc+4e-3|M6K|6DsjF!FlAu9-ZRT9xu9MkU z?LHlsmASqhFR&wI0ECH5=X~U8weMcE-d>N?k3 z3n90k^D8|YIEAmycNexk?>>9nkh2_i2JEkH2T2nl24bgWCuPYZ?a=n33dcjrJ&|=5H*@Z)!HIhfTfj4`><}kX_w zz{?ZEFQW&JFYJ4u%>U)>JwwLJpWq2x-Buce*)N#|Q#?eu;uc73wg6FS>ZK~lrtt+) z7+soI{^|MAMhB9@vaCqs1&iHl#lDOuR331Cb~PIxG-qi=(nBw-a#4GmW%~BwW80zP zLglVmZ>ap;pUDXZkicgfuiVsuL(b52Oe`43$2<(Cn4QLBlyUdiFr64K0RFG zvEu}E^k}(SXt}-(I)-Fh-PjLTR}@#vp1smsUG1Sf?s7~SwiTsHvha#v3-chRKLy7{ zL@W(pGi;kA8tW<}$~riZp2eWk&||^gYB;&nVa&vjJI^zS35|Urwp@QRBzW_>(z z5$S|DfZRX`TAn3foG2Ax2i0=!L>%sy?r&lU_6VBpImRVAhp7ucC1R`0xpStLm85&& zxkKy6GaK*2(z-Mnl0jsb509j0vl2n21IlB&C>9jM-#^o;E7UN2K;61JDd54UUeA13 zdqQD}tIbWi2JS;7y_8en&YYLnHKn=2p?SOM; zt{Hjd)zttf&!U)fRw4Ey;`U-9LItISLqEL5cjc3VcE{{x31uV7$HLbI(viY(XW~@l z>D>PZe>iH!1v;3|If14Y&|lq;P<&Ql(sokP%u%Lztc#k~=H&ecK>pvG0vyqxw0%~p zK#nXT7u19X{}Z;GU!p7jwM)i-f$fIw4icBcwKza2J3p{e;o7O$!{2xz@Qsiml^4zk zT0^9|z^(BN4Vd=WqAG)7{apiJJnU^>H0d)|M!$Z|*qK1r$*r>=MV7N?+42Doonr^f zCVl{)#NR~7Q6o2@L>|!KB49|~JppRGxjM;I2C&BVK?we_J7l~O)ZrA9shWmAp8vtW zAM*2K{QQ_de$1ag){h_S=YM4XP&7%Ua|91$1QrhXQ=lej;mpf?W9?e9r}+%04(^|I zUC`>xDIcqO|7bQ{+=cm6VtOPClP&!b8T9GF-*o-&$D?yQfEwVM@CbD28+0R+ebMvJ zwguon9{lu6-15J6TR8H0fI5MxMkj}P68N{kFuQY4h&`TBCp*D?o77=KVKao1Ej%{;^4+HwM9t~0 zTLEL<-117z79Ihmbq#TpqM9>oc_f|IZUHYx7k8G$CF7U zQqse^tP51Bn`YoqMBZg5?hjnmRBdA#E{G4c@SPWJ^=Z|govj3&JKn2c#8oK-Z?>W_ zW?k1n%mW=Y?Wwji4T+5Xrx~BVtuz$tbhgzf(9Gel-mPpx2WlN1JY!><+C=py?pj^# zW{`!9z=?eW_iYEH__t1kt4F0b(EvkZjWOOff##1OmJ-GLl9JUFF^NuG?KE+s=flMk zk2LedENcTucP_m-l6*4qfk^lMAg{GXpI)KuP?6<_psz^059tP@r}pyCi;=ZX!dO?d zl|LYC?ECyj4>X++MtpIy{)$wcHZe#nSZi+W-@T{#gpnE@X@>A+=1CSldDDrIvvLS` zJC`7o6QiwELd@(x;>N+WVQrlpAXeBd!FX>sy#ePzUpmxdUB-xhObyHJCOMYQWGuX; znFr6(_Rt@)G8zz%6nGb^RISASw%Pxj$MB1M);Frq$7?fr71CXs!NeX2nJmGH_-c8_X zgNl1>)n*H#9o}7hqH@Kn+|F(|*mCxg>x~rA49DQh3N6!34(&Pk^RSQDktt+yga(46 zqjo|r_LZ2))saBq_ya+fom25hC8>j*ig$oSIrfW;pKJSI=~`cw8AvV>A3`QOJu$os z5z-%4$U*qZuC`38ktFq(nwBrq&Q}f=HEL`r76}OQ8WsD|L zOFf7p#aoz-o-&J+;-%zk!=@4bTr!>4OP)u~9Zej(uPC*h7sr(R{$AVbz$Dc=@v7>Z zPHyo?xXQ<^R}&2Qu69uA3e)Sq0GdS;X0lDWDYA$4!#AzQrQ2Psdr;$R)NSNlu1Tec zP=pIEb_EU=b0Bh%&Y)WzUV2&)kyGLNuRB0QO{RHozSv#WAbc8oQ~hMV$ozHSb57O5YsY2! zYvzABpzLpuAHN-1_D}MTs_;C0?CJEH2HXeJMyA-JL$}`jS3*61w0d>JYFXNH=)>&1 zMOyA_L86;3&YZ6qRMFPKt%{jR&yOgIMjb1Qy|Jgx&UyTDf=uv%zB{%G=F9F3E8%zM z6-sBdzMkP*fhB0_s`ZIe*@SWzu-xdt&F2f5+&SfD*N-H9)e#|Ca!6V`^}U|;9v+M@ zfi>T)I@v5`i2e)0S9NqZB-cUUgT)ISCapNYr+q`v%S`WVexx{{=uJK2~zs8==O=`f9`YX`jdKN~Ji)z4rCWC;JJ*Gia3Co~o$)Er9vE1RjNh zF6Y+{U}!ID5HvKoE0$LLY0u!!Ao6F=g5uaea6m+AfgwNN7{i~2{1!3(8`S1sikkpU zM|76d)-J0TS!V`uNpo17n8KxW{el8cAy;i)Lq@4Xj$1dIn5N*dAr>DZAoI0iLU;W3 zpa)h8YT|k;AQSb9rLEZ9}MO`i@&2Dp44gh$HiN ziSQZ2Xf>pW$8dlAZSR1rUGTglbN1Eq4<)T?Q&pL-87(+5X6|eBE_Ir8M?XJ#KdDm7 z)W@}L|Nd*%Cui7?bU>FQG(clh;{Bg7Lgvh8plr=TV{j5Q#=nF-uAnA2=Egc+)Y0hr zuyx(5am)+4*f|qDmxb@e$+cplCd9x{@xxPFG1VCM1u0LnO(p4mMMb1Kp`Ki4Wpn)% z;|zFW^G6aS5i>92`WsX!xpQ)Zuku%@60lfOVe`jkSZqSYksnU={DM@HXLy=l4|_oz zqef}KenBdIBzuFH8gZ4MkV?7SPvd4Ix@O=ztfR=Efl3e;0-D0;g~5Y{&TZ*Pt*46V zKbrA_8{*$VY(He<|Hw$8YX;x<-6x>!90txMg?Jk~TYKTO>qYSo;aBXwG-ka|Nztx( z-*KFN_W8k^_2t;#R+jHaUi_V3y#DXjvRqEs-ywNW-hYSp0p`J2$U!h_b^ccvB*T9V zltak=7b__hlESTUtHU&$f?j?E}5)#J@CG6EFis4+i;1{!d5+4b9sA|_7QAw#5 z19mR`^taCTCl#04&SSkhQKhf<)K=ellA+%xli->x6=}}2sF(=W$I^lk)@Capn-YVN zQl+^|o7W~6wo>T%Lp=tyeexoYxb9B4;dHa%RE#mJl|`x_akV^t#W7l2!scn%0b#|TAv^|HxfFKRM#lLt+?#`6~ z2b;K-8+NjlNqQXJ55?^)Pgz|nO#f65sD5Us|97KTn>Ll?sQg?(R+?xVqgpf@rw3R2 z-4fV7&H03aGO-Wfswrm%hR1=DN#@F-0ZHa8t_IgJ_*)A!V0{Y-LaeI3gQ1`}HZaue z%h&IJPL9D1jJmoOQs8fmW)<`V(_$*aWJHpx%U_7*N>L{;Mos3 z`!P2Ee>H1BaCGZPo#a!FIJxCcs!xO%_j=q;&9Zas5u3&NYK~^T$WWoauWMnU8z(dtNi_ zesOf$nEIgX3tWi$8d;f>M3r+OUnQX1U>u87l^XZY@%Byq-fkG1SSg3#RnhRi6>)Cg z2iw<=LwZqUL5IyjFx=?nvFV^r%xG7;l==WFLSPIL9P#FQnMWpgK%dkaKO5F&3(^IvOkS=h=ywc|j}!0XCtIn~BR=I^w|0_>3$RY( zCRcl&x32XF96Mz*`|<2}lZ(*kVh$6Pd+9DBX~Z6%ofs^V;F2U6zVCUI!H`SLAx=+( zC~MvM+pL2ob&TSM66P#Zv0bR!$ZF{Fn-a1cJ}{P`-`=n@K?VlH!kONeYg?KYFKt$- zNd?@AlkceZlHgR*GnB9ckwC z_MPb8hp8QapT8;S%PZb(t$QK~hYSH!S2F9fTFhDLD+nL^4i8sEC*l!%zsBahP6a z=Jma;z1R1x*4o##*IM7&-*;Vm`^)vb^UTcad7t0&yXSfC-+ePKMC5-QsC;1WxpKQC z)!W;z;L*d=`X|gob7^sj#|2a1PqQKU3O=#~uMR$j6jv_7z~8MT=_lH%;hEj2QAQSHTVDHsIP zoft!JY#1S_=QD)7H7ClE;^Z539=i*UZ~qH%3+`j4G}M89ysH_*3vaH9a6%Y!O$OA+ zTBB%%+c`83J(85(4r$>+Z@w1i=u_XReRJPSddjIIcd|w=DtizNHmiv2#8gChjZ}r{ zCP-(QR2~;QaO#vb!g{x$1P^D0gJfDe5Q#0dlvKfrTthZUWvf$K$GCJR}~dV^{im-Pp0J zx~_|twmsf_LMCs1)1f1+AKH&5U*z+-*Sh^0S_ax1n8GSz*v#@V23d;T95*_4)8tjR zCc9cwKe`vX`LwJ+ehDr*c>L(xW&dQMZIy9C{Iz%<_2s9%xTPKzl1ZfyuftTvxlp_Z zvkn#t>BBqt<%>OZnk3!8+77jeCdzimv6}0l^A8^1tvfJ&4IwR0x(>K__ghlPNZ-uw zGt@xWQ7ena8WA0jG~JeX50!`{=jz&6|LYW~v9`C_`=^I|XD&^P{NC6L~6?TF-D+#%UcHck) zDFg9UPxnt}CCMKg95xeeAKb9P*jj^Xbeu_Lg@ZcCM8LHgQpH)Ac0)R#bHmFq#}l@$ zy^Dz(z$mLQ$of)FV#fy6b6%3o4=F|?!CGiHf-vDZP1INvBwz$Byu6F2WPuo6qy%_{ zW?dO`^8PzdE9G*P>{1oIj{ObsR9uda^a6=U{0ImTkkkHkie-KH9oa(-bXNS*tQdD zBx)&{n^Ti`=jOZkCmq}j$uQk|+pap}&j^&o@8s|OlB4?qRVCPxnSfpAJ0}b#juLKX zzndgEsW3j>EFSupe55-^cDT$rciF$x?6LOjQ;&7!hc=;iBDJh&yLuqlcJM~umo0;& zRm9XD-3f99s(}nT(U8S>A#OJJHp_csV1VNSClif+D_faY@ZM|q?m}J1`%9f|WdFXw zifh4szt#NZU))bFGZH3g?kIst`>;{Nl#yhgjVkOoD-7==bI2(D4 z{8K7)f6z7)jQ@1vv@libG<`P^+a5%6o_b;}X!o1llzy8_^q1{;K|Zg7)+}6F>6@+9 zl$VW`;$~hu+ilw74O)r>gp*?6(~R$1;WCK~eHzGj8TP-NegvO(V8Nrlnag9acIP#H z)E76)ubMTF)c9`y@_lfEUHhQ_8M3u8TAN3|jEJ=m`F~$VP{PYMtsZ%9-=UcBaqdOz zOVcfq8=F%H>c!eK?%OziU10O8L_&~G)<}POetsyNqB;4tqiZHidGCsL5@=)hCn^fe z{#rE8_yC&j<2rj82}Np58X(jopxb$#W!L>I-Ak-}?X35rLL~|CK0kR>fPvxiz|NEk zXlQ#PXi_V_iZ~FA;s_?fT#X-95ZAh_jmz3(SPPQ1m0@i;TuT%GS-J66pi+aUsaB#3 zOomQG!J8{E+~EHrH{2l`EwxgZDRuOY|VK!Y2b2K+}ptvb-wpD^>^6EqanY8oOy}WuxtLlbs zXaD^O896Ra@#SuQH~apG;z^|eHQt%?xq0{N^Z&ahK=3C|u^rzI+=)Si_RK#bURv+o z;~#@~u6K=<$Bnysx~1Xp`-C?;LKPpNRZ_bQ~Uj7T=O}?wq&E zyY~Uhm%T=>y4+FE9#qZf)$iw6!N(8|ne1h6n3#=oZpfV1@dTEpJz&2dj8pmn1zi>( z4s=OPK#&9ctAD__lkUC3`wrd&=l~!?rw%hjAoc6daA%{V$&(^Js|Y|7&SSH|ni4!? zSU09Vm*)~N_ny3pkk6ihw!`&cnw?w@3Fezu&Qw?n#)o7!(-!%wR}o~`Dum;o_!vxw zu9486=V>r=om&3-AKeOeg%2+JqrqCy9RFuuRi>jg;4T#;Lv<7%amO&AN2yMtb- z7aO@QpWD*!(bpQ9`Qg-GeeX{W%x3lgoP&~V8CV+X@fe1~TLyD6R?zgV#J8PLM5Zh- zA_<=;s8X2|W(0VsHxjo&cEE^5J5VhBY5Ehov=h9p!P0@BwOdOj$nOk$d@TaaCfKq} zu88rTFJe$A2tyou6@#Wrj-xwvalS$K#Rd3%M!j|Wjo*r53eTh7r$Fma)8>XX?8F%z zNKu#@2RqW(G4mlFTewmxBxIB=4>bIR>_!|6@-xJIe+AqB@v6a2l7WAru*sEeJ{5=R z&Lp?AWo)3T9Mcvs2d)`#dsh+RA+Uf|WCwHiEL?=(I02>D+H^88QTxnA)QPc8K06v5 zaeh-c`DN@zdg1x3Be_M3Z=uG?2QeH`cIoe~wD{UvjBoYt4BbB(k39@GPTW8y_v!#J zd6Jq(O%2q6%)KzsGcWYwj_DqEz7g8s*ch?3Gt5Z_^vfr_;%l0wqvXMfql|*|<~!8Q z{?-mO(<^go34OsHA_Wot9QL8-_0KiNoz`DKtjCSD!!bke?!8p3N za8t14O%JI6-o9C_ofE;SV`)->{rGAXJFzDXCp0jSFu*w3lBr;XW;i!0h^914p1rRq@ z23zZ?E83p2LvPah%5-Zil$xA9NUJ_|7@MY48h=(B%ZGYFbT7|BjBxVh=fcrAT0faCpa1s-?^fvG%!ygVCnt)3ZCzn~+56YMTyn@9C1bNq9dd zanvfRdA$u~07yIwrf28J&&=*zy60g1TB+*rQ7Ng@cf5E%>fNw(FSAzB{bWdI2?zVf(3uzunLod0KgR|?}^rL6*8(?H);)^vZfp^F{Kpz+Bt z+2Bov8n+H`LGyF$fH0@+%;HMo^{vmsk3PXFjuy=lOR9qOdWMV4yM@Mw)6d^=^+^iF z-qRzmGuCk8YzEDkrnZb%59`#K6D_lbbX4?CsBK$ykEXXMeO=9g-1|~`8XjJ|QbV_F z-y*Nk&bd4~7oDNNz40Z~8w@YiNe8B!VZu7vcoTYMx30TyYVK=YX%6P0l^^a==OaaK zGP2yl2lYew^YUzy2=nDDJnT#_CUw+os58E3w$j(bSnf$|Gw%IiD6FO1a0J=5u!_h& zl1!Kj5JUx@4Wu@s2B|Czcn%ghT@R*H>UJm&QfiLi2JG!TK9fFg^0@S{adxCK%r1-6 zRN|T{wt27e#seHWvx>x2WnBdmVWsjty&SaC7Zc8Hd3 zUUjIf$l*=&w~s}l64mNUuH0wZe-8J2+Qymg8!wCE@i7}e+`=PlOX z-pAbLh5D`=dg(i*eA4wzo8*%%{rIJ=5eV))BQu7hGe#Df8rEYta%A%o3kS`DoP#{+ zwpyAK*37moMA5h$Nu@B2`@~GNs|YRDK5?dJ1P70wtV(_)$5k)heWfM!e60Ml;(n95 zJ!U%#!h0p}?pQY2dZ8}-K2r8eQcC|>Qu;AV{rtlJO_o|k7?KB_A&d^4#!X2345e6M zC!?_w0ql!F+XpIG!-WjCQ#gj15y~`h^z8fQ1E!K9>(Qlt^xUg4AwRXlj`8gobh76w1=ao@TRZzR z9X+p?4egdM;x8fl7c2*gHMtIpMt9~$`0q{3P)E=)#+xj;rp?2TQap>9Rcbo4183*NMJZ;kXDEL?WM9oKFR(WJEG z26?+)3cFyVvwWfyc=~-ixcB0m^n|nlzMvX!=}qt)B!((4p0IMTEk3q#S6y8*KUjqX z+N3zvI53Q*7%4Ml%*Pbj-o9Ha-GW zZVIBVA|78VW2@ou)VlQ_ZI06%ratRv#&^G5m$?7Y5vFpo@}|S%PcQC^e(v`PK?&(# zd*NL`FD9`8l4GK-)jB9Vpo#j~E{2>R9vulAYt{)muF6r~c#wC`tPjgu{o-8m-fjWs zg)-u8@<`U}$*YO8L1UQf#^Qa8&j2%KLaqHrdDYdY27zUGsBa;msA|4X#-C=2iB1T_p%MFR=?&5dteP zUilE14mCgR ziQv&6(Rv4#BjOExdq5%GEGUJuo$v)qyAmOO7~n}~L*i%{uWQ3knRgJ@nz0tld+h{^ zA_!%|pj-xCoI<@|8mSsge(QwcwrC}c@v2}5F2OTZVUr3py20pPz#gvobO4w!=usUQ zwex8L3{5gfL0t8c{D5Vo!mvE_4jGi9an8fc)8BtX0{dHG?9<7`!@8q6j5n1h1`M-x zDND;I_rJ@)&9JkDC3qH3@eLPu{WH0WKjrm*w9s+IG?s63VI(~g374p75xO$AgVhYv z<`p@6?-=ii-V2p|E>FVTwJzOlQ!j`V>g{@NcZ-w5N0M`a?u|wUgmskN9@cvhcI$-I zZ1&sb5|xJ2&Bw#CxAa3%3*u&kk3h3Axyp?zIxGSYe#abiARF4Y#C?pOyuiRh0$5<4 zpFo9bUC@g%$mwysH*32ggn!H^iTN_QuZI8^J6B|CH^UDyg~kcB;FwOcF92OHN5~ve zxRzsF7)_0fg2K0P@!@Q7`Bj9m9cc5Lz7sXwVhU?r+<@v#xX}slw&YHZ25|@j>Qmv= z&`e{g3|=uF{5To#?}YWi4;fwrthdr zmdKgH;1G=$ICrnKgLnSoa$E7j1^4Xw`Fd-lsu8|ZnwemQ-QL`l6&;ta)~_-@zh5p} njnYHu`C+PGiIowfM*PsUc=mq{)qDQtl>A47^xyS;tNs549q94J literal 0 HcmV?d00001 diff --git a/docs/images/north_audit.jpg b/docs/images/north_audit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7b49e13fb71ba57f615c67d7d5e6d743f0fd4c8a GIT binary patch literal 6539 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 Date: Mon, 21 Oct 2024 14:11:50 +0530 Subject: [PATCH 85/91] FOGL-9122: Create JSON array datapoint if not blank (#1472) * FOGL-9122: Create JSON array datapointif not blank Signed-off-by: nandan --- C/common/reading_set.cpp | 9 +++++++-- C/services/north/data_load.cpp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/C/common/reading_set.cpp b/C/common/reading_set.cpp index 1e7d7e6642..77c24078bc 100755 --- a/C/common/reading_set.cpp +++ b/C/common/reading_set.cpp @@ -654,8 +654,13 @@ Datapoint *rval = NULL; arrayValues.push_back(i); } } - DatapointValue value(arrayValues); - rval = new Datapoint(name, value); + + // Don't create blank array of datapoint values + if (!arrayValues.empty()) + { + DatapointValue value(arrayValues); + rval = new Datapoint(name, value); + } break; } diff --git a/C/services/north/data_load.cpp b/C/services/north/data_load.cpp index 081ac74ede..0d7b41289f 100755 --- a/C/services/north/data_load.cpp +++ b/C/services/north/data_load.cpp @@ -301,7 +301,7 @@ ReadingSet *DataLoad::fetchAudit(unsigned int blockSize) { const Condition conditionId(GreaterThan); // WHERE id > lastId - Where* wId = new Where("id", conditionId, to_string(m_lastFetched + 1)); + Where* wId = new Where("id", conditionId, to_string(m_lastFetched)); vector columns; // Add colums and needed aliases columns.push_back(new Returns("id")); From cea35a4a67417673ff02c96142cfc6662a78a432 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 22 Oct 2024 14:46:56 +0100 Subject: [PATCH 86/91] FOGL-9206 Correct image names (#1474) * FOGL-9206 Correct image names Signed-off-by: Mark Riddoch * Add title Signed-off-by: Mark Riddoch --------- Signed-off-by: Mark Riddoch --- docs/monitoring/configuration.rst | 2 +- docs/monitoring/flow.rst | 6 +++--- docs/monitoring/introduction.rst | 3 +++ docs/monitoring/resources.rst | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/monitoring/configuration.rst b/docs/monitoring/configuration.rst index 3b5df26b83..6403b6c738 100644 --- a/docs/monitoring/configuration.rst +++ b/docs/monitoring/configuration.rst @@ -48,7 +48,7 @@ Each time any user modifies a configuration item a Slack message will be sent to This is a very simple message that only gives the information that a change has been made, however more sophisticated delivery mechanisms can be used that will detail the actual change. Sending To External Systems --------------------------- +--------------------------- Audit log data can also be sent to the north in the same way that reading data can. This can be used to send data to third party systems to maintain a change log of internal Fledge changes in other systems. In order to send audit log data to the north we merely setup a new north service or task. When we configure the north plugin we select the data source as *audit* rather than readings or statistics. diff --git a/docs/monitoring/flow.rst b/docs/monitoring/flow.rst index f32a20499e..57220f8412 100644 --- a/docs/monitoring/flow.rst +++ b/docs/monitoring/flow.rst @@ -1,7 +1,7 @@ -.. |MonitorNorthRate| image:: ../imagesMonitorNorthRate.jpg -.. |MonitorZendesk| image:: ../imagesMonitorZendesk.jpg +.. |MonitorNorthRate| image:: ../images/MonitorNorthRate.jpg +.. |MonitorZendesk| image:: ../images/MonitorZendesk.jpg .. |MonitorTrigger| image:: ../images/MonitorTrigger.jpg -.. |MonitorBuffered| image:: ../images/MonitorBuffered.jpg +.. |MonitoredBuffered| image:: ../images/MonitoredBuffered.jpg Pipeline Data Flow ================== diff --git a/docs/monitoring/introduction.rst b/docs/monitoring/introduction.rst index 12fbb2eba6..939b0641e9 100644 --- a/docs/monitoring/introduction.rst +++ b/docs/monitoring/introduction.rst @@ -1,3 +1,6 @@ +Introduction +============ + Monitoring within Fledge is a varied subject and has different meanings to the different user personas that are using Fledge. Monitoring can take the form of diff --git a/docs/monitoring/resources.rst b/docs/monitoring/resources.rst index b43941549b..0895c0cea7 100644 --- a/docs/monitoring/resources.rst +++ b/docs/monitoring/resources.rst @@ -1,4 +1,4 @@ -.. |MonitorDiskUsge| image:: ../images/MonitorDiskUsge.jpg +.. |MonitorDiskUsage| image:: ../images/MonitorDiskUsage.jpg Resources ========= @@ -36,9 +36,9 @@ In this example we will assume we want to use the notification service to monito Since we are looking for a numeric value to go above a certain value, say 85%, we can simply use the threshold notification rule to detect the disk usage going above this value and deliver a notification using any of the Fledge notification delivery mechanisms. -+====================+ ++--------------------+ | |MonitorDiskUsage| | -+====================+ ++--------------------+ Database Disk Usage =================== From 969e4b447e3c478c0d662f287d6afe20239ddeea Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 23 Oct 2024 09:38:44 +0100 Subject: [PATCH 87/91] FOGL-9157 Correct delete user documentation (#1475) Signed-off-by: Mark Riddoch --- docs/rest_api_guide/02_RESTauthentication.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/rest_api_guide/02_RESTauthentication.rst b/docs/rest_api_guide/02_RESTauthentication.rst index e3785f24b2..ef2bd31d42 100644 --- a/docs/rest_api_guide/02_RESTauthentication.rst +++ b/docs/rest_api_guide/02_RESTauthentication.rst @@ -408,11 +408,12 @@ Delete User ``DELETE /fledge/admin/user/{userID}/delete`` - delete a user + +The delete user call can only be made by users with administrator privileges. If a user that is currently logged in is removed then that user will be forcibly logged out of the system. + .. note:: - - It is not possible to remove the user that is currently logged in to the system. - - Only Admin can delete the user. - - Super Admin cannot be deleted. + The user with the user name admin can not be removed from the system. **Example** From a3479f9362138983248f95ef8b71faec99c8daac Mon Sep 17 00:00:00 2001 From: dianomicbot Date: Thu, 24 Oct 2024 09:30:38 +0000 Subject: [PATCH 88/91] VERSION changed Signed-off-by: dianomicbot --- VERSION | 2 +- docs/91_version_history.rst | 112 ++++++++++++++++++++++++++++++++++++ docs/conf.py | 2 +- python/setup.py | 2 +- 4 files changed, 115 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index dc165f3307..4ba94eff21 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -fledge_version=2.5.0 +fledge_version=2.6.0 fledge_schema=74 diff --git a/docs/91_version_history.rst b/docs/91_version_history.rst index 012e388074..f1fe95e3bc 100644 --- a/docs/91_version_history.rst +++ b/docs/91_version_history.rst @@ -24,6 +24,118 @@ Version History Fledge v2 ========== +v2.6.0 +------- + +Release Date: 2024-10-24 + +- **Fledge Core** + + - New Features: + + - A new section relating to all aspects of monitoring within Fledge has been added to the documentation + - Monitoring of the available space on the disk that holds the SQLite buffer for readings ingested by Fledge has been added. Entries will be written to the error log to predict the expiration of the disk space and also logs when the spaces left available falls below 10% and 5%. + - Failures to send data north via the north service raises an alert, this is now cleared if the flow of data north later resumes. + - An issue that could cause high CPU utilisation when a north service was unable to send data to the upstream system has been resolved. The user is also made more aware of failed attempts to send data upstream using the alerting feature. An alert will be created and shown in the GUI status bar. + - The tuning documentation has been updated to discuss the use of the configuration cache size tuning parameter and to add a discussion on tuning the log level for the various services. + - It is now possible to create allow and block lists for IP addresses that are allowed access or explicitly denied access, to the API port of the instance. + - The configuration items within configuration category now have the ability to limit the user roles that are allowed to update the configuration item. + - Configuration items can now be given a permission property that can be used to control which user roles have access to the configuration item. + - Performance monitors have been improved. + + + - Bug Fix: + + - The documentation for deleting users via the REST API has been corrected. + - An issue that caused incorrect payloads to be sent north when choosing the audit log as the data source has been resolved. + - An issue that incorrectly prevented some authorised users executing a control end point has been resolved. + - An issue with backup and restore when using Postgres as the storage engine to store the configuration data has been resolved. + - The manual purge, in the developer features of the user interface, was not working for assets containing ' #', ‘+' or '&’ characters in asset name. + - An issue with north services that have been misconfigured bot cleanly shutting down has been resolved. + - An issue that could cause a backup file to not download has been resolved. + - An issue that could allow an unauthorised user to change the password of another user has been resolved. + - After restart of Fledge, the Sent count in the Fledge GUI dashboard would increase by few readings after every restart of Fledge even if no south plugins were running. This has been fixed. This was an error in the increment of the counter; there were never any additional readings sent. + - An issue with an incorrect audit entry being created when adding new properties to a configuration item has been resolved. + - A security issue that could allow one user to see the profile of another has been resolved. + - Users now need to have administration permissions to see the user names and roles of other users. + - The handling of errors during pipeline creation has been enhanced to give better reporting of filter plugin exceptions. + - The notification service has been updated to allow sub-second retriever times for notifications to be specified. + + +- **GUI** + + - New Features: + + - The GUI rendering of lists in the configuration options has been improved. + - The ordering of the tabs in configuration user interface is no longer alphabetical, but controlled by the configuration category itself. + - The user interface now hides configuration options the user does not have permission to change. + - The rendering of the developer menu has been bough into line with other sub-menu rendering. + + + - Bug Fix: + + - An issue that causes the sidebar to fail to open when connecting to a FogLAMP User Interface from FogLAMP Manage has been resolved. + - An issue with the user interface incorrectly displaying a timestamp has been resolved. + - An issue with list type configuration items occasionally not rendering correctly for filters has been resolved. + - An issue with the GUI not correctly checking the length of some items entered into configuration items has been resolved. + - An issue with forcible session disconnection sometimes failing has been resolved. + - The performance of the GUI related to management of services and plugins has been improved. + - An issue that prevent export of persisted data in developer mode has been resolved. + - An issue that prevented deletion of persisted data for plugins in developer mode has been resolved. + - An issue with the CSV writer filter has been resolved. + + +- **Plugins** + + - New Features: + + - OMF North: The _action_ code in the HTTP header is normally set to _update_ for OMF Data messages. This causes old values to be updated if the timestamps match, and new data to be properly compressed by the PI Data Archive. If the AVEVA PI Buffer Subsystem is configured, however, the _update_ action code is converted to an internal PI storage mode that causes new data to bypass compression which can result in unnecessarily storing too many data values. To work around this issue, the OMF North plugin now allows you to change the OMF Data action code to _create_. This should only be done when the AVEVA PI Buffer Subsystem is configured. + - The SKF Observer south plugin has been updated to correctly ingest multi-channel trends into separately named assets. + - A new filter has been added that allows data anomalies to be superimposed on data in a pipeline. This is used to test downstream anomaly detection and is not intended for production use. + - The asset filter has been enhanced to allow regulator expressions to be used in the asset name to match when applying the filter. + - The MQTT scripted north plugin has been enhanced to support macros in the topic to which data is sent. These macros allow the name of the asset and/or the values of any of the datapoints within a reading to be substituted into the topic string. + - The Observer south plugin now creates asset names by using the sub-machine names, if defined in Observer. This is required if the machine name and measurement point pairs are not unique in order to disambiguate the naming of the assets within the readings. + - A new, global asset name prefix can now be defined for the Observer south plugin. + - An enhancement to the FFT2 filter has been made that allows for periodic calculation of the FFT. + - The error reporting for failed authentication and incorrectly configured connections within the FogLAMP Observer south plugin has been improved to reduce the number of duplicate errors written to the system log. + - The documentation for the normalisation filter, that is used to interpolate date to defined time intervals, has been updated. + - The delta filter has been enhanced to give greater control over the action when one or more datapoint in an asset is detected as changing. + - The SigmaCleanse filter has been enhanced to allow an option that outliers be labeled rather than removed. The default behaviour still removes the outliers from the pipeline. + - Support for the Micro8xx PLC has been improved in the EthernetIP south plugin. + - The logging in the MQTT Scripted south plugin has been improved and the frequency with which MQTT broker failure is reported has been reduced to prevent flooding the error log with messages. + - The Regex filter has been enhanced to allow the matched strings to be reused in the substituted strings. It also now has an option to control the scope of the regular expression matching. + - A new single asset model filter has been added that creates a semantic model based on data from one or more sources into a single asset structure. + - The Siemens S7 south plugin documentation has been updated to include a note regarding the use of optimized block access. + - The documentation of the EthernetIP plugin has been updated to include the currently supported types that can be read from the PLC. + - A new filter, the Bounds Status filter, has been added that checks for data being within certain limits and adds meta data to the reading to indicate the status. + - A new north plugin and corresponding hint filter have been created to send data into the AVEVA eDNA historian. + - The fog lamp-south-south-rest plugin has been updated to support numeric timestamps. A numeric timestamp is taken as the number of seconds since the Linux epoch, 1st January 1970. + - fledge-south-s2opcua: Support for the OPC UA Data Change Filter has been added. This filter type is defined in the [OPC UA Specification, Part 4, Section 7.22.2|https://reference.opcfoundation.org/Core/Part4/v105/docs/7.22.2]. The Data Change Filter allows OPC UA clients (such as this plugin) to request that the OPC UA server send data value updates only if the server's data values have changed significantly. With careful tuning, you can reduce the data traffic from OPC UA server to client without significant loss of fidelity. This plugin has been upgraded to use Systerel’s [S2OPC Toolkit Version 1.5.0|https://gitlab.com/systerel/S2OPC/-/releases/S2OPC_Toolkit_1.5.0]. + - The Operation Notification plugin now support data substitution as with the other control related notification delivery plugins. + + + - Bug Fix: + + - The MQTT North Scripted plugin has been updated such that if the topic string can not be substituted fully the data will not be sent. + - The support for different datatypes in the Sparkplug B south plugin has been improved. + - The error reporting and recovery in the Observer south plugin has been improved to give more detailed information regarding failures and to better tailor error recovery to cause of the error. + - The random walk south plugin has been improved to give a more random result. + - I problem with the dynamic reconfiguration of the EthernetIP south plugin when changing between reading per tag and reading per asset modes has been resolved. + - An issue with changing the IP address of the Observer service in the Observer south plugin not being dynamically actioned has been resolved. + - Improvements have been made to the filtering features of the Observer south plugin. + - An issue with the DT9837A south plugin that could cause buffer overrun and stop the data ingesting has been resolved. + - An issue with the way the MQTT Scripted north plugin handled failure of the MQTT broker has been resolved. The plugin is now more resilient to broker failure and able to recover correctly when the broker is available again. + - The EthernetIP south plugin documentation has been updated to stress the requirement that the type in the data map matches the type used in the PLC code. + - The EthernetIP plugin now allows the action to be taken if an error is encountered reading data from the PLC to be configured. + - (not for the release notes) The fledge-south-s2opcua documentation file had broken references to subsections 'OPC UA Subscriptions' and 'Subscriptions'. They have been fixed. + - An issue with the EthernetIP plugin not correctly recognising network disconnections has been resolved. + - Support has been added to the EthernetIP plugin for readings PLC tags that contain an array of boolean values. + - fledge-south-s2opcua: the plugin would sometimes fail to connect to an OPC UA server with a large number of available endpoints on a distant or noisy network. This has been fixed. + - The north notification plugin used to implement conditional forwarding of data via north tasks has been updated to support complex pipelines with branches and parallel processing. + - An issue that could cause a crash during purge operations has been resolved. + - An issue with the MODBUS-C plugin that could cause it to fail if the IP address of the MODBUS device was changed incorrectly and then changed back to the correct address has been resolved. + + v2.5.0 ------- diff --git a/docs/conf.py b/docs/conf.py index 551a59b4c3..f815b90c6b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -178,4 +178,4 @@ # Pass Plugin DOCBRANCH argument in Makefile ; by default develop # NOTE: During release time we need to replace DOCBRANCH with actual released version -subprocess.run(["make generated DOCBRANCH='develop'"], shell=True, check=True) +subprocess.run(["make generated DOCBRANCH='2.6.0RC'"], shell=True, check=True) diff --git a/python/setup.py b/python/setup.py index 9738d3b4e5..a7a8c241a5 100644 --- a/python/setup.py +++ b/python/setup.py @@ -3,7 +3,7 @@ setup( name='Fledge', python_requires='>=3.6.9', - version='2.5.0', + version='2.6.0', description='Fledge, the open source platform for the Internet of Things', url='https://github.com/fledge-iot/fledge', author='OSIsoft, LLC; Dianomic Systems Inc.', From ca10b58c27742346e02216cc8b2fca18a3e4c397 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Oct 2024 13:01:03 +0530 Subject: [PATCH 89/91] fledge specific documentation updates for v2.6.0 Signed-off-by: ashish-jabble --- docs/91_version_history.rst | 86 +++++++++++-------------------------- 1 file changed, 26 insertions(+), 60 deletions(-) diff --git a/docs/91_version_history.rst b/docs/91_version_history.rst index f1fe95e3bc..19b6ed6a71 100644 --- a/docs/91_version_history.rst +++ b/docs/91_version_history.rst @@ -33,107 +33,73 @@ Release Date: 2024-10-24 - New Features: - - A new section relating to all aspects of monitoring within Fledge has been added to the documentation - - Monitoring of the available space on the disk that holds the SQLite buffer for readings ingested by Fledge has been added. Entries will be written to the error log to predict the expiration of the disk space and also logs when the spaces left available falls below 10% and 5%. - - Failures to send data north via the north service raises an alert, this is now cleared if the flow of data north later resumes. + - Monitoring of the available space on the disk that holds the SQLite buffer for readings ingested by Fledge has been added. Entries will be written to the error log to predict the expiration of the disk space and also logs when the spaces left available fall below 10% and 5%. + - Failures to send data north via the north service raise an alert, this is now cleared if the flow of data north later resumes. - An issue that could cause high CPU utilisation when a north service was unable to send data to the upstream system has been resolved. The user is also made more aware of failed attempts to send data upstream using the alerting feature. An alert will be created and shown in the GUI status bar. - - The tuning documentation has been updated to discuss the use of the configuration cache size tuning parameter and to add a discussion on tuning the log level for the various services. - - It is now possible to create allow and block lists for IP addresses that are allowed access or explicitly denied access, to the API port of the instance. - - The configuration items within configuration category now have the ability to limit the user roles that are allowed to update the configuration item. + - It is now possible to create allow and block lists for IP addresses that are allowed access or explicitly denied access to the API port of the instance. + - The configuration items within the configuration category now have the ability to limit the user roles that are allowed to update the configuration item. - Configuration items can now be given a permission property that can be used to control which user roles have access to the configuration item. - Performance monitors have been improved. - + - A new section relating to all aspects of monitoring within Fledge has been added to the documentation + - The tuning documentation has been updated to discuss the use of the configuration cache size tuning parameter and to add a discussion on tuning the log level for the various services. - Bug Fix: - - The documentation for deleting users via the REST API has been corrected. - An issue that caused incorrect payloads to be sent north when choosing the audit log as the data source has been resolved. - - An issue that incorrectly prevented some authorised users executing a control end point has been resolved. + - An issue that incorrectly prevented some authorised users from executing a control endpoint has been resolved. - An issue with backup and restore when using Postgres as the storage engine to store the configuration data has been resolved. - The manual purge, in the developer features of the user interface, was not working for assets containing ' #', ‘+' or '&’ characters in asset name. - - An issue with north services that have been misconfigured bot cleanly shutting down has been resolved. + - An issue with north services that have been misconfigured not cleanly shutting down has been resolved. - An issue that could cause a backup file to not download has been resolved. - An issue that could allow an unauthorised user to change the password of another user has been resolved. - - After restart of Fledge, the Sent count in the Fledge GUI dashboard would increase by few readings after every restart of Fledge even if no south plugins were running. This has been fixed. This was an error in the increment of the counter; there were never any additional readings sent. + - After restart of Fledge, the sent count in the Fledge GUI dashboard would increase by few readings after every restart of Fledge, even if no south plugins were running. This has been fixed. This was an error in the increment of the counter; there were never any additional readings sent. - An issue with an incorrect audit entry being created when adding new properties to a configuration item has been resolved. - A security issue that could allow one user to see the profile of another has been resolved. - Users now need to have administration permissions to see the user names and roles of other users. - The handling of errors during pipeline creation has been enhanced to give better reporting of filter plugin exceptions. - The notification service has been updated to allow sub-second retriever times for notifications to be specified. + - The documentation for deleting users via the REST API has been corrected. - **GUI** - New Features: - - The GUI rendering of lists in the configuration options has been improved. - - The ordering of the tabs in configuration user interface is no longer alphabetical, but controlled by the configuration category itself. + - The rendering of lists in the configuration options has been improved. + - The ordering of the tabs in the configuration user interface is no longer alphabetical but controlled by the configuration category itself. - The user interface now hides configuration options the user does not have permission to change. - - The rendering of the developer menu has been bough into line with other sub-menu rendering. + - The rendering of the developer menu has been brought into line with other sub-menu rendering. - Bug Fix: - - An issue that causes the sidebar to fail to open when connecting to a FogLAMP User Interface from FogLAMP Manage has been resolved. - An issue with the user interface incorrectly displaying a timestamp has been resolved. - An issue with list type configuration items occasionally not rendering correctly for filters has been resolved. - - An issue with the GUI not correctly checking the length of some items entered into configuration items has been resolved. + - An issue related to improper length validation for certain entries in the configuration items has been resolved. - An issue with forcible session disconnection sometimes failing has been resolved. - - The performance of the GUI related to management of services and plugins has been improved. - - An issue that prevent export of persisted data in developer mode has been resolved. + - The performance related to management of services and plugins has been improved. + - An issue that prevents export of persisted data in developer mode has been resolved. - An issue that prevented deletion of persisted data for plugins in developer mode has been resolved. - - An issue with the CSV writer filter has been resolved. + - An issue with the plugin filter that involved underscores in the name has been resolved. - **Plugins** - New Features: - - OMF North: The _action_ code in the HTTP header is normally set to _update_ for OMF Data messages. This causes old values to be updated if the timestamps match, and new data to be properly compressed by the PI Data Archive. If the AVEVA PI Buffer Subsystem is configured, however, the _update_ action code is converted to an internal PI storage mode that causes new data to bypass compression which can result in unnecessarily storing too many data values. To work around this issue, the OMF North plugin now allows you to change the OMF Data action code to _create_. This should only be done when the AVEVA PI Buffer Subsystem is configured. - - The SKF Observer south plugin has been updated to correctly ingest multi-channel trends into separately named assets. - - A new filter has been added that allows data anomalies to be superimposed on data in a pipeline. This is used to test downstream anomaly detection and is not intended for production use. - - The asset filter has been enhanced to allow regulator expressions to be used in the asset name to match when applying the filter. - - The MQTT scripted north plugin has been enhanced to support macros in the topic to which data is sent. These macros allow the name of the asset and/or the values of any of the datapoints within a reading to be substituted into the topic string. - - The Observer south plugin now creates asset names by using the sub-machine names, if defined in Observer. This is required if the machine name and measurement point pairs are not unique in order to disambiguate the naming of the assets within the readings. - - A new, global asset name prefix can now be defined for the Observer south plugin. - - An enhancement to the FFT2 filter has been made that allows for periodic calculation of the FFT. - - The error reporting for failed authentication and incorrectly configured connections within the FogLAMP Observer south plugin has been improved to reduce the number of duplicate errors written to the system log. - - The documentation for the normalisation filter, that is used to interpolate date to defined time intervals, has been updated. - - The delta filter has been enhanced to give greater control over the action when one or more datapoint in an asset is detected as changing. - - The SigmaCleanse filter has been enhanced to allow an option that outliers be labeled rather than removed. The default behaviour still removes the outliers from the pipeline. - - Support for the Micro8xx PLC has been improved in the EthernetIP south plugin. - - The logging in the MQTT Scripted south plugin has been improved and the frequency with which MQTT broker failure is reported has been reduced to prevent flooding the error log with messages. - - The Regex filter has been enhanced to allow the matched strings to be reused in the substituted strings. It also now has an option to control the scope of the regular expression matching. - - A new single asset model filter has been added that creates a semantic model based on data from one or more sources into a single asset structure. - - The Siemens S7 south plugin documentation has been updated to include a note regarding the use of optimized block access. - - The documentation of the EthernetIP plugin has been updated to include the currently supported types that can be read from the PLC. - - A new filter, the Bounds Status filter, has been added that checks for data being within certain limits and adds meta data to the reading to indicate the status. - - A new north plugin and corresponding hint filter have been created to send data into the AVEVA eDNA historian. - - The fog lamp-south-south-rest plugin has been updated to support numeric timestamps. A numeric timestamp is taken as the number of seconds since the Linux epoch, 1st January 1970. - - fledge-south-s2opcua: Support for the OPC UA Data Change Filter has been added. This filter type is defined in the [OPC UA Specification, Part 4, Section 7.22.2|https://reference.opcfoundation.org/Core/Part4/v105/docs/7.22.2]. The Data Change Filter allows OPC UA clients (such as this plugin) to request that the OPC UA server send data value updates only if the server's data values have changed significantly. With careful tuning, you can reduce the data traffic from OPC UA server to client without significant loss of fidelity. This plugin has been upgraded to use Systerel’s [S2OPC Toolkit Version 1.5.0|https://gitlab.com/systerel/S2OPC/-/releases/S2OPC_Toolkit_1.5.0]. - - The Operation Notification plugin now support data substitution as with the other control related notification delivery plugins. - + - fledge-filter-asset now supports to allow regulator expressions to be used in the asset name to match when applying the filter. + - fledge-filter-delta has been enhanced to give greater control over the action when one or more datapoints in an asset are detected as changing. + - fledge-notify-operation now supports data substitution as with the other control-related notification delivery plugins. + - fledge-north-OMF *action* code in the HTTP header is normally set to *update* for OMF Data messages. This causes old values to be updated if the timestamps match and new data to be properly compressed by the PI Data Archive. If the AVEVA PI Buffer Subsystem is configured, however, the *update* action code is converted to an internal PI storage mode that causes new data to bypass compression, which can result in unnecessarily storing too many data values. To work around this issue, the OMF North plugin now allows you to change the OMF Data action code to *create*. This should only be done when the AVEVA PI Buffer Subsystem is configured. + - fledge-south-s2opcua now supports for the OPC UA Data Change Filter has been added. This filter type is defined in the `OPC UA Specification, Part 4, Section 7.22.2 `_. The Data Change Filter allows OPC UA clients (such as this plugin) to request that the OPC UA server send data value updates only if the server's data values have changed significantly. With careful tuning, you can reduce the data traffic from OPC UA server to the client without significant loss of fidelity. This plugin has been upgraded to use Systerel's `S2OPC Toolkit Version 1.5.0 `_. - Bug Fix: - - The MQTT North Scripted plugin has been updated such that if the topic string can not be substituted fully the data will not be sent. - - The support for different datatypes in the Sparkplug B south plugin has been improved. - - The error reporting and recovery in the Observer south plugin has been improved to give more detailed information regarding failures and to better tailor error recovery to cause of the error. - - The random walk south plugin has been improved to give a more random result. - - I problem with the dynamic reconfiguration of the EthernetIP south plugin when changing between reading per tag and reading per asset modes has been resolved. - - An issue with changing the IP address of the Observer service in the Observer south plugin not being dynamically actioned has been resolved. - - Improvements have been made to the filtering features of the Observer south plugin. - - An issue with the DT9837A south plugin that could cause buffer overrun and stop the data ingesting has been resolved. - - An issue with the way the MQTT Scripted north plugin handled failure of the MQTT broker has been resolved. The plugin is now more resilient to broker failure and able to recover correctly when the broker is available again. - - The EthernetIP south plugin documentation has been updated to stress the requirement that the type in the data map matches the type used in the PLC code. - - The EthernetIP plugin now allows the action to be taken if an error is encountered reading data from the PLC to be configured. - - (not for the release notes) The fledge-south-s2opcua documentation file had broken references to subsections 'OPC UA Subscriptions' and 'Subscriptions'. They have been fixed. - - An issue with the EthernetIP plugin not correctly recognising network disconnections has been resolved. - - Support has been added to the EthernetIP plugin for readings PLC tags that contain an array of boolean values. - - fledge-south-s2opcua: the plugin would sometimes fail to connect to an OPC UA server with a large number of available endpoints on a distant or noisy network. This has been fixed. - - The north notification plugin used to implement conditional forwarding of data via north tasks has been updated to support complex pipelines with branches and parallel processing. - An issue that could cause a crash during purge operations has been resolved. - - An issue with the MODBUS-C plugin that could cause it to fail if the IP address of the MODBUS device was changed incorrectly and then changed back to the correct address has been resolved. + - fledge-south-modbusc plugin that could cause it to fail if the IP address of the MODBUS device was changed incorrectly and then changed back to the correct address has been resolved. + - fledge-south-mqtt-sparkplug now supports for different datatypes in the Sparkplug B south plugin has been improved. + - fledge-south-randomwalk has been improved to give a more random result. + - fledge-south-s2opcua would sometimes fail to connect to an OPC UA server with a large number of available endpoints on a distant or noisy network. This has been fixed. v2.5.0 From d0297c967fca5c02e2460aa88fb7be19d1ff8d35 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Oct 2024 18:37:44 +0530 Subject: [PATCH 90/91] feedback fixes Signed-off-by: ashish-jabble --- docs/91_version_history.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/91_version_history.rst b/docs/91_version_history.rst index 19b6ed6a71..1025aec185 100644 --- a/docs/91_version_history.rst +++ b/docs/91_version_history.rst @@ -40,7 +40,7 @@ Release Date: 2024-10-24 - The configuration items within the configuration category now have the ability to limit the user roles that are allowed to update the configuration item. - Configuration items can now be given a permission property that can be used to control which user roles have access to the configuration item. - Performance monitors have been improved. - - A new section relating to all aspects of monitoring within Fledge has been added to the documentation + - A new section relating to all aspects of monitoring within Fledge has been added to the documentation. - The tuning documentation has been updated to discuss the use of the configuration cache size tuning parameter and to add a discussion on tuning the log level for the various services. - Bug Fix: @@ -80,24 +80,25 @@ Release Date: 2024-10-24 - The performance related to management of services and plugins has been improved. - An issue that prevents export of persisted data in developer mode has been resolved. - An issue that prevented deletion of persisted data for plugins in developer mode has been resolved. - - An issue with the plugin filter that involved underscores in the name has been resolved. + - An issue with the filter plugin that involved underscores in the name has been resolved. - **Plugins** - New Features: - - fledge-filter-asset now supports to allow regulator expressions to be used in the asset name to match when applying the filter. - - fledge-filter-delta has been enhanced to give greater control over the action when one or more datapoints in an asset are detected as changing. + - fledge-filter-asset now supports to allow regular expressions to be used in the asset name to match when applying the filter. + - fledge-filter-delta has been enhanced to give greater control over the action when one or more datapoints in an asset reading are detected as changing. - fledge-notify-operation now supports data substitution as with the other control-related notification delivery plugins. - - fledge-north-OMF *action* code in the HTTP header is normally set to *update* for OMF Data messages. This causes old values to be updated if the timestamps match and new data to be properly compressed by the PI Data Archive. If the AVEVA PI Buffer Subsystem is configured, however, the *update* action code is converted to an internal PI storage mode that causes new data to bypass compression, which can result in unnecessarily storing too many data values. To work around this issue, the OMF North plugin now allows you to change the OMF Data action code to *create*. This should only be done when the AVEVA PI Buffer Subsystem is configured. + - fledge-north-OMF *action* code in the HTTP header is normally set to *update* for OMF Data messages. This causes old values to be updated if the timestamps match and new data to be properly compressed by the PI Data Archive. + If the AVEVA PI Buffer Subsystem is configured, however, the *update* action code is converted to an internal PI storage mode that causes new data to bypass compression, which can result in unnecessarily storing too many data values. To workaround this issue, the OMF North plugin now allows you to change the OMF Data action code to *create*. This should only be done when the AVEVA PI Buffer Subsystem is configured. - fledge-south-s2opcua now supports for the OPC UA Data Change Filter has been added. This filter type is defined in the `OPC UA Specification, Part 4, Section 7.22.2 `_. The Data Change Filter allows OPC UA clients (such as this plugin) to request that the OPC UA server send data value updates only if the server's data values have changed significantly. With careful tuning, you can reduce the data traffic from OPC UA server to the client without significant loss of fidelity. This plugin has been upgraded to use Systerel's `S2OPC Toolkit Version 1.5.0 `_. - Bug Fix: - An issue that could cause a crash during purge operations has been resolved. - fledge-south-modbusc plugin that could cause it to fail if the IP address of the MODBUS device was changed incorrectly and then changed back to the correct address has been resolved. - - fledge-south-mqtt-sparkplug now supports for different datatypes in the Sparkplug B south plugin has been improved. + - fledge-south-mqtt-sparkplug now supports various data types, including string, integer, float and boolean. - fledge-south-randomwalk has been improved to give a more random result. - fledge-south-s2opcua would sometimes fail to connect to an OPC UA server with a large number of available endpoints on a distant or noisy network. This has been fixed. From 0df98379a95e921c093c6961c11951f5e37fc4de Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Mon, 28 Oct 2024 11:31:07 +0530 Subject: [PATCH 91/91] minor fixes Signed-off-by: ashish-jabble --- docs/91_version_history.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/91_version_history.rst b/docs/91_version_history.rst index 1025aec185..43d7cdf4ea 100644 --- a/docs/91_version_history.rst +++ b/docs/91_version_history.rst @@ -34,7 +34,7 @@ Release Date: 2024-10-24 - New Features: - Monitoring of the available space on the disk that holds the SQLite buffer for readings ingested by Fledge has been added. Entries will be written to the error log to predict the expiration of the disk space and also logs when the spaces left available fall below 10% and 5%. - - Failures to send data north via the north service raise an alert, this is now cleared if the flow of data north later resumes. + - Failures to send data north via the north service raise an alert. This is now cleared if the flow of data north later resumes. - An issue that could cause high CPU utilisation when a north service was unable to send data to the upstream system has been resolved. The user is also made more aware of failed attempts to send data upstream using the alerting feature. An alert will be created and shown in the GUI status bar. - It is now possible to create allow and block lists for IP addresses that are allowed access or explicitly denied access to the API port of the instance. - The configuration items within the configuration category now have the ability to limit the user roles that are allowed to update the configuration item. @@ -90,9 +90,8 @@ Release Date: 2024-10-24 - fledge-filter-asset now supports to allow regular expressions to be used in the asset name to match when applying the filter. - fledge-filter-delta has been enhanced to give greater control over the action when one or more datapoints in an asset reading are detected as changing. - fledge-notify-operation now supports data substitution as with the other control-related notification delivery plugins. - - fledge-north-OMF *action* code in the HTTP header is normally set to *update* for OMF Data messages. This causes old values to be updated if the timestamps match and new data to be properly compressed by the PI Data Archive. - If the AVEVA PI Buffer Subsystem is configured, however, the *update* action code is converted to an internal PI storage mode that causes new data to bypass compression, which can result in unnecessarily storing too many data values. To workaround this issue, the OMF North plugin now allows you to change the OMF Data action code to *create*. This should only be done when the AVEVA PI Buffer Subsystem is configured. - - fledge-south-s2opcua now supports for the OPC UA Data Change Filter has been added. This filter type is defined in the `OPC UA Specification, Part 4, Section 7.22.2 `_. The Data Change Filter allows OPC UA clients (such as this plugin) to request that the OPC UA server send data value updates only if the server's data values have changed significantly. With careful tuning, you can reduce the data traffic from OPC UA server to the client without significant loss of fidelity. This plugin has been upgraded to use Systerel's `S2OPC Toolkit Version 1.5.0 `_. + - fledge-north-OMF plugin's *action* code in the HTTP header is typically set to *update* for OMF Data messages. This setting allows old values to be updated when timestamps match and ensures that new data is compressed correctly by the PI Data Archive. However, if the AVEVA PI Buffer Subsystem is configured, the *update* action code is converted to an internal PI storage mode, which causes new data to bypass compression and may lead to excessive storage of too many data values. To address this issue, the OMF North plugin now allows you to change the OMF Data action code to *create*. This option should only be used when the AVEVA PI Buffer Subsystem is configured. + - fledge-south-s2opcua now supports for the OPC UA Data Change Filter. This filter type is defined in the `OPC UA Specification, Part 4, Section 7.22.2 `_. The Data Change Filter allows OPC UA clients (such as this plugin) to request that the OPC UA server send data value updates only if the server's data values have changed significantly. With careful tuning, you can reduce the data traffic from OPC UA server to the client without significant loss of fidelity. This plugin has been upgraded to use Systerel's `S2OPC Toolkit Version 1.5.0 `_. - Bug Fix: