-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Managed Network Fabric CLI- Fix output formatting for resync-password and rotate-certificate commands #9755
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
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b023fa5
Resync-password and rotate-certificate output formatting Fix
nafizhaider32 e232f8a
Address PR review: preserve --no-wait semantics, fix module collision…
nafizhaider32 5150b0c
Fix pylint no-name-in-module errors for custom package imports
nafizhaider32 6f9dcfc
formatting fix
nafizhaider32 727a0c2
some test updates
nafizhaider32 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/managednetworkfabric/azext_managednetworkfabric/custom/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
| import importlib.util | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| def _load_legacy_custom_module(): | ||
| """Load and return the sibling ``custom.py`` module for backward compatibility.""" | ||
| legacy_module_path = Path(__file__).resolve().parent.parent / "custom.py" | ||
| if not legacy_module_path.is_file(): | ||
| return None | ||
|
|
||
| module_name = __name__ + "._legacy_custom" | ||
| spec = importlib.util.spec_from_file_location(module_name, str(legacy_module_path)) | ||
| if spec is None or spec.loader is None: | ||
| return None | ||
|
|
||
| module = importlib.util.module_from_spec(spec) | ||
| spec.loader.exec_module(module) | ||
| return module | ||
|
|
||
|
|
||
| _legacy_custom_module = _load_legacy_custom_module() | ||
|
|
||
| if _legacy_custom_module is not None: | ||
| for _name in dir(_legacy_custom_module): | ||
| if not _name.startswith("_"): | ||
| globals()[_name] = getattr(_legacy_custom_module, _name) | ||
|
|
||
| __all__ = [name for name in dir(_legacy_custom_module) if not name.startswith("_")] | ||
| else: | ||
| __all__ = [] | ||
36 changes: 36 additions & 0 deletions
36
src/managednetworkfabric/azext_managednetworkfabric/custom/device_resync_password.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # | ||
| # -------------------------------------------------------------------------------------------- | ||
| # pylint: disable=protected-access,duplicate-code | ||
|
|
||
| """ | ||
| This code inherits the auto-generated code for device resync-password command, and adds | ||
| custom error formatting. | ||
| """ | ||
|
|
||
| from azure.core.exceptions import HttpResponseError | ||
|
|
||
| from azext_managednetworkfabric.aaz.latest.networkfabric.device import ( | ||
| ResyncPassword as _ResyncPasswordCommand, | ||
| ) | ||
| from azext_managednetworkfabric.operations.error_format import ErrorFormat | ||
|
|
||
|
|
||
| class ResyncPasswordCommand(_ResyncPasswordCommand): | ||
| """Custom class for networkfabric device resync-password""" | ||
|
|
||
| def _handler(self, command_args): | ||
| poller = super()._handler(command_args) | ||
| if poller is None: | ||
| return None | ||
| if self.ctx.args.no_wait: | ||
| return poller | ||
| try: | ||
| return poller.result() | ||
| except HttpResponseError as e: | ||
| ErrorFormat.handle_lro_error(e) | ||
|
nafizhaider32 marked this conversation as resolved.
nafizhaider32 marked this conversation as resolved.
|
||
|
|
||
| def _output(self, *args, **kwargs): | ||
| return ErrorFormat._output(self, *args, **kwargs) | ||
36 changes: 36 additions & 0 deletions
36
src/managednetworkfabric/azext_managednetworkfabric/custom/fabric_resync_password.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # | ||
| # -------------------------------------------------------------------------------------------- | ||
| # pylint: disable=protected-access,duplicate-code | ||
|
|
||
| """ | ||
| This code inherits the auto-generated code for fabric resync-password command, and adds | ||
| custom error formatting. | ||
| """ | ||
|
|
||
| from azure.core.exceptions import HttpResponseError | ||
|
|
||
| from azext_managednetworkfabric.aaz.latest.networkfabric.fabric import ( | ||
| ResyncPassword as _ResyncPasswordCommand, | ||
| ) | ||
| from azext_managednetworkfabric.operations.error_format import ErrorFormat | ||
|
|
||
|
|
||
| class ResyncPasswordCommand(_ResyncPasswordCommand): | ||
| """Custom class for networkfabric fabric resync-password""" | ||
|
|
||
| def _handler(self, command_args): | ||
| poller = super()._handler(command_args) | ||
| if poller is None: | ||
| return None | ||
| if self.ctx.args.no_wait: | ||
| return poller | ||
| try: | ||
| return poller.result() | ||
| except HttpResponseError as e: | ||
| ErrorFormat.handle_lro_error(e) | ||
|
nafizhaider32 marked this conversation as resolved.
nafizhaider32 marked this conversation as resolved.
|
||
|
|
||
| def _output(self, *args, **kwargs): | ||
| return ErrorFormat._output(self, *args, **kwargs) | ||
36 changes: 36 additions & 0 deletions
36
src/managednetworkfabric/azext_managednetworkfabric/custom/fabric_rotate_certificate.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # | ||
| # -------------------------------------------------------------------------------------------- | ||
| # pylint: disable=protected-access,duplicate-code | ||
|
|
||
| """ | ||
| This code inherits the auto-generated code for fabric rotate-certificate command, and adds | ||
| custom error formatting. | ||
| """ | ||
|
|
||
| from azure.core.exceptions import HttpResponseError | ||
|
|
||
| from azext_managednetworkfabric.aaz.latest.networkfabric.fabric import ( | ||
| RotateCertificate as _RotateCertificateCommand, | ||
| ) | ||
| from azext_managednetworkfabric.operations.error_format import ErrorFormat | ||
|
|
||
|
|
||
| class RotateCertificateCommand(_RotateCertificateCommand): | ||
| """Custom class for networkfabric fabric rotate-certificate""" | ||
|
|
||
| def _handler(self, command_args): | ||
| poller = super()._handler(command_args) | ||
| if poller is None: | ||
| return None | ||
| if self.ctx.args.no_wait: | ||
| return poller | ||
| try: | ||
| return poller.result() | ||
| except HttpResponseError as e: | ||
| ErrorFormat.handle_lro_error(e) | ||
|
nafizhaider32 marked this conversation as resolved.
nafizhaider32 marked this conversation as resolved.
|
||
|
|
||
| def _output(self, *args, **kwargs): | ||
| return ErrorFormat._output(self, *args, **kwargs) | ||
86 changes: 86 additions & 0 deletions
86
src/managednetworkfabric/azext_managednetworkfabric/operations/error_format.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # | ||
| # Custom code that is added in addition to auto-generated by aaz-dev code. | ||
| # -------------------------------------------------------------------------------------------- | ||
| # pylint: disable=too-many-lines,no-member,inconsistent-return-statements | ||
| # pylint: disable=too-many-statements,too-few-public-methods | ||
|
|
||
| """ | ||
| Helper class for formatting error responses with nested details. | ||
|
|
||
| The Azure SDK ODataV4Format.message_details() concatenates error detail entries | ||
| without newline separators between them, causing all details to run together on | ||
| single lines. This helper intercepts the HttpResponseError raised by the LRO | ||
| poller and rebuilds the error message with proper formatting. | ||
| """ | ||
|
|
||
| from knack.util import CLIError | ||
|
|
||
|
|
||
| class ErrorFormat: | ||
| """Helper class for formatting error responses with nested details""" | ||
|
|
||
| @staticmethod | ||
| def _output(parent_cmd, *args, **kwargs): # pylint: disable=unused-argument | ||
| """Custom response transform that prevents nested error details from being duplicated""" | ||
| result = parent_cmd.deserialize_output( | ||
| parent_cmd.ctx.vars.instance, client_flatten=True | ||
| ) | ||
|
|
||
| if result and isinstance(result, dict) and "error" in result: | ||
| error = result.get("error") | ||
| if error and isinstance(error, dict): | ||
| if "details" in error and isinstance(error["details"], list): | ||
| for detail in error["details"]: | ||
| if isinstance(detail, dict) and "details" in detail: | ||
| del detail["details"] | ||
|
|
||
| return result | ||
|
|
||
| @staticmethod | ||
| def format_error_message(http_error): | ||
| """Build a properly formatted error message with newlines between detail entries. | ||
|
|
||
| Reproduces the ODataV4Format layout but fixes the missing newline between | ||
| each detail block so they no longer run together on a single line. | ||
| """ | ||
| error = getattr(http_error, "error", None) | ||
| if not error: | ||
| return str(http_error) | ||
|
|
||
| code = getattr(error, "code", None) or "Unknown" | ||
| message = getattr(error, "message", None) or str(http_error) | ||
|
|
||
| lines = [ | ||
| f"({code}) {message}", | ||
| f"Code: {code}", | ||
| f"Message: {message}", | ||
| ] | ||
|
|
||
| target = getattr(error, "target", None) | ||
| if target: | ||
| lines.append(f"Target: {target}") | ||
|
|
||
| details = getattr(error, "details", None) | ||
| if details: | ||
| lines.append("Exception Details:") | ||
| for i, detail in enumerate(details): | ||
| if i > 0: | ||
| lines.append("") | ||
| d_code = getattr(detail, "code", None) or "Unknown" | ||
| d_msg = getattr(detail, "message", None) or "" | ||
| lines.append(f"\t({d_code}) {d_msg}") | ||
| lines.append(f"\tCode: {d_code}") | ||
| lines.append(f"\tMessage: {d_msg}") | ||
| d_target = getattr(detail, "target", None) | ||
| if d_target: | ||
| lines.append(f"\tTarget: {d_target}") | ||
|
|
||
| return "\n".join(lines) | ||
|
|
||
| @staticmethod | ||
| def handle_lro_error(http_error): | ||
| """Raise a CLIError with a properly formatted error message.""" | ||
| raise CLIError(ErrorFormat.format_error_message(http_error)) from http_error |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.