Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix slash in path. #3573

Merged

Conversation

xincunli-sonic
Copy link
Contributor

@xincunli-sonic xincunli-sonic commented Oct 11, 2024

What I did

Addressing issue #20377. The issue caused by unescape in JsonPointer implementation which followed RFC 6901

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 will handle it correctly.

How to verify it

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.

Previous command output (if the output of a command-line utility has changed)

New command output (if the output of a command-line utility has changed)

parts = pointer.parts

# Re-escapes `/` in path parts by converting `/` back to `~1`.
parts = [part.replace("/", "~1") for part in pointer.parts]
Copy link
Contributor

@wen587 wen587 Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it an issue for multi-asic only?
I remember in single asic, the jsonpointer has built in feature to parse / to ~1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was introduced by multi asic change, we have to extract the namespace from the patch, then JsonPointer will do escape ~1 back to /

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. I don't get it.
By the code here, the extract_scope doesn't differentiate single or multi. How do we guarantee single asic is not broken?

sonic-utilities/config/main.py

Lines 1599 to 1600 in 319f58d

for change in patch:
scope, modified_path = extract_scope(change["path"])

Copy link
Contributor Author

@xincunli-sonic xincunli-sonic Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For single asic, the portchannel interface does not have slash in port, but multi asic does have, hence, we could do escape slash to ~1 in jsonpatch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another reason is single asic logic is taking path as input parameter directly, but multi asic, we have to do combine with parts by Jsonpointer result..

wen587
wen587 previously approved these changes Oct 23, 2024
@qiluo-msft
Copy link
Contributor

qiluo-msft commented Oct 28, 2024

        except Exception:

Can we use specific (not too general) exception type? #Closed


Refers to: tests/generic_config_updater/multiasic_change_applier_test.py:80 in 26ee2a4. [](commit_id = 26ee2a4, deletion_comment = False)

@qiluo-msft
Copy link
Contributor

        except Exception:

still applicable.


In reply to: 2442871334


Refers to: tests/generic_config_updater/multiasic_change_applier_test.py:80 in 26ee2a4. [](commit_id = 26ee2a4, deletion_comment = False)

@xincunli-sonic
Copy link
Contributor Author

xincunli-sonic commented Oct 29, 2024

discussed offline, change to specific error handling to avoid unexpected errors.

@qiluo-msft qiluo-msft merged commit 7d013df into sonic-net:master Nov 8, 2024
7 checks passed
@mlok-nokia
Copy link
Contributor

@xincunli-sonic This PR has cherry-pick conflict. Please help to address it so that can cherry-pick to 202405. Thanks

xincunli-sonic added a commit to xincunli-sonic/sonic-utilities that referenced this pull request Nov 26, 2024
#### What I did

Addressing issue [#20377](sonic-net/sonic-buildimage#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.
```
@xincunli-sonic
Copy link
Contributor Author

xincunli-sonic commented Nov 26, 2024

This requires the previous PR be cherry picked into 202405, after it done, I will reuquest the current PR backport again.

@mlok-nokia
Copy link
Contributor

mlok-nokia commented Nov 27, 2024

This requires the previous PR be cherry picked into 202405, after it done, I will reuquest the current PR backport again.

Thanks.

xincunli-sonic added a commit to xincunli-sonic/sonic-utilities that referenced this pull request Dec 4, 2024
Addressing issue [#20377](sonic-net/sonic-buildimage#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"""

    _RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$')
    _RE_INVALID_ESCAPE = re.compile('(~[^01]|~$)')

    def __init__(self, pointer):

        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
```

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.

```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.
```
xincunli-sonic added a commit that referenced this pull request Dec 4, 2024
Addressing issue [#20377](sonic-net/sonic-buildimage#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"""

    _RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$')
    _RE_INVALID_ESCAPE = re.compile('(~[^01]|~$)')

    def __init__(self, pointer):

        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
```

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.

```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.
```
@bingwang-ms
Copy link
Contributor

Included in 202405 by PR #3649

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants