Skip to content

Commit 7d013df

Browse files
Fix slash in path. (#3573)
#### What I did Addressing issue [#20377](#20377). The issue caused by unescape in JsonPointer implementation which followed [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901) ```python pointer = jsonpointer.JsonPointer(path) ... class JsonPointer(object): """A JSON Pointer that can reference parts of a JSON document""" # Array indices must not contain: # leading zeros, signs, spaces, decimals, etc _RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$') _RE_INVALID_ESCAPE = re.compile('(~[^01]|~$)') def __init__(self, pointer): # validate escapes invalid_escape = self._RE_INVALID_ESCAPE.search(pointer) if invalid_escape: raise JsonPointerException('Found invalid escape {}'.format( invalid_escape.group())) parts = pointer.split('/') if parts.pop(0) != '': raise JsonPointerException('Location must start with /') parts = [unescape(part) for part in parts] self.parts = parts ``` #### How I did it Re-escape `/` to `~1` to match the real key in json, and [JsonPatch](https://www.rfc-editor.org/rfc/rfc6902#appendix-A.14) will handle it correctly. #### How to verify it ```shell admin@str2-7250-lc1-2:~$ cat link.json [ { "op": "add", "path": "/asic1/PORTCHANNEL_INTERFACE/PortChannel106|10.0.0.6~131", "value": {} } ] admin@str2-7250-lc1-2:~$ sudo config apply-patch link.json sonic_yang(6):Note: Below table(s) have no YANG models: DHCP_SERVER sonic_yang(6):Note: Below table(s) have no YANG models: LOGGER sonic_yang(6):Note: Below table(s) have no YANG models: LOGGER Patch Applier: asic1: Patch application starting. Patch Applier: asic1: Patch: [{"op": "add", "path": "/PORTCHANNEL_INTERFACE/PortChannel106|10.0.0.6~131", "value": {}}] Patch Applier: asic1 getting current config db. Patch Applier: asic1: simulating the target full config after applying the patch. Patch Applier: asic1: validating all JsonPatch operations are permitted on the specified fields Patch Applier: asic1: validating target config does not have empty tables, since they do not show up in ConfigDb. Patch Applier: asic1: sorting patch updates. Patch Applier: The asic1 patch was converted into 0 changes. Patch Applier: asic1: applying 0 changes in order. Patch Applier: asic1: verifying patch updates are reflected on ConfigDB. Patch Applier: asic1 patch application completed. Patch applied successfully. ```
1 parent 0af4386 commit 7d013df

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

generic_config_updater/generic_updater.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717

1818
def extract_scope(path):
1919
if not path:
20-
raise Exception("Wrong patch with empty path.")
20+
raise GenericConfigUpdaterError("Wrong patch with empty path.")
2121
pointer = jsonpointer.JsonPointer(path)
22-
parts = pointer.parts
22+
23+
# Re-escapes
24+
parts = [jsonpointer.escape(part) for part in pointer.parts]
2325
if not parts:
2426
raise GenericConfigUpdaterError("Wrong patch with empty path.")
2527
if parts[0].startswith("asic"):

tests/generic_config_updater/multiasic_change_applier_test.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import jsonpointer
12
import unittest
23
from importlib import reload
34
from unittest.mock import patch, MagicMock
45
from generic_config_updater.generic_updater import extract_scope
6+
from generic_config_updater.generic_updater import GenericConfigUpdaterError
57
import generic_config_updater.change_applier
68
import generic_config_updater.services_validator
79
import generic_config_updater.gu_common
@@ -49,6 +51,12 @@ def test_extract_scope_multiasic(self, mock_is_multi_asic):
4951
"/asic0123456789/PORTCHANNEL/PortChannel102/admin_status": (
5052
True, "asic0123456789", "/PORTCHANNEL/PortChannel102/admin_status"
5153
),
54+
"/asic1/PORTCHANNEL_INTERFACE/PortChannel106|10.0.0.6/31": (
55+
True, "asic1", "/PORTCHANNEL_INTERFACE/PortChannel106|10.0.0.6/31"
56+
),
57+
"/asic1/PORTCHANNEL_INTERFACE/PortChannel106|10.0.0.6~131": (
58+
True, "asic1", "/PORTCHANNEL_INTERFACE/PortChannel106|10.0.0.6~131"
59+
),
5260
"/localhost/BGP_DEVICE_GLOBAL/STATE/tsa_enabled": (
5361
True, "localhost", "/BGP_DEVICE_GLOBAL/STATE/tsa_enabled"
5462
),
@@ -95,7 +103,11 @@ def test_extract_scope_multiasic(self, mock_is_multi_asic):
95103
scope, remainder = extract_scope(test_path)
96104
assert(scope == expectedscope)
97105
assert(remainder == expectedremainder)
98-
except Exception:
106+
except AssertionError:
107+
assert(not result)
108+
except GenericConfigUpdaterError:
109+
assert(not result)
110+
except jsonpointer.JsonPointerException:
99111
assert(not result)
100112

101113
@patch('sonic_py_common.multi_asic.is_multi_asic')
@@ -158,7 +170,11 @@ def test_extract_scope_singleasic(self, mock_is_multi_asic):
158170
scope, remainder = extract_scope(test_path)
159171
assert(scope == expectedscope)
160172
assert(remainder == expectedremainder)
161-
except Exception:
173+
except AssertionError:
174+
assert(not result)
175+
except GenericConfigUpdaterError:
176+
assert(not result)
177+
except jsonpointer.JsonPointerException:
162178
assert(not result)
163179

164180
@patch('generic_config_updater.change_applier.get_config_db_as_json', autospec=True)

0 commit comments

Comments
 (0)