From ed5d207a55253004a5984424075a000994b5ab2b Mon Sep 17 00:00:00 2001 From: Allen Robel Date: Tue, 2 Dec 2025 10:23:33 -1000 Subject: [PATCH 1/3] Cleanup docstrings This commit (and PR) does not contain any functional changes. It merely cleans up docstrings to conform to proper Markdown and adds module docstrings. --- .../module_utils/bootflash/bootflash_files.py | 402 ++++++++++-------- .../module_utils/bootflash/bootflash_info.py | 119 +++--- .../bootflash/convert_file_info_to_target.py | 272 +++++++----- .../bootflash/convert_target_to_params.py | 127 ++++-- plugins/modules/dcnm_bootflash.py | 173 ++++---- 5 files changed, 637 insertions(+), 456 deletions(-) diff --git a/plugins/module_utils/bootflash/bootflash_files.py b/plugins/module_utils/bootflash/bootflash_files.py index 31c279866..c5d9ca8a7 100644 --- a/plugins/module_utils/bootflash/bootflash_files.py +++ b/plugins/module_utils/bootflash/bootflash_files.py @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +Delete files from bootflash devices. +""" from __future__ import absolute_import, division, print_function __metaclass__ = type # pylint: disable=invalid-name @@ -30,25 +33,30 @@ class BootflashFiles: """ - ### Summary + # Summary + Delete files from bootflash devices. - ### Raises - - ``ValueError`` if: - - ``rest_send`` is not set before calling commit() - - ``results`` is not set before calling commit() - - ``switch_details`` is not set before calling commit() - - payload.deleteFiles is empty when calling commit() - - ``filename`` is not set before calling add_file() - - ``filepath`` is not set before calling add_file() - - ``ip_address`` is not set before calling add_file() - - ``supervisor`` is not set before calling add_file() - - ``switch_details`` is not set before calling add_file() - - ``TypeError`` if: - - ``switch_details`` is not an instance of ``SwitchDetails``. - - ip_address to serial_number conversion fails. - - ### Usage + ## Raises + + ### ValueError + + - `rest_send` is not set before calling commit() + - `results` is not set before calling commit() + - `switch_details` is not set before calling commit() + - payload.deleteFiles is empty when calling commit() + - `filename` is not set before calling add_file() + - `filepath` is not set before calling add_file() + - `ip_address` is not set before calling add_file() + - `supervisor` is not set before calling add_file() + - `switch_details` is not set before calling add_file() + + ### TypeError + + - `switch_details` is not an instance of `SwitchDetails`. + - ip_address to serial_number conversion fails. + + ## Usage ```python sender = Sender() @@ -84,7 +92,7 @@ class BootflashFiles: instance.commit() ``` - ### Payload Structure + ## Payload Structure The structure of the request body to delete bootflash files. @@ -146,13 +154,16 @@ def __init__(self) -> None: def refresh_switch_details(self) -> None: """ - ### Summary + # Summary + If switch details are not already refreshed, refresh them. - ### Raises - - ``ValueError`` if: - - ``switch_details`` is not set. - - ``rest_send`` is not set. + ## Raises + + ### ValueError + + - `switch_details` is not set. + - `rest_send` is not set. """ method_name: str = inspect.stack()[0][3] @@ -172,12 +183,15 @@ def refresh_switch_details(self) -> None: def ip_address_to_serial_number(self, ip_address: str) -> str: """ - ### Summary + # Summary + Convert ip_address to serial_number. - ### Raises - - ``ValueError`` if: - - switch_details is not set. + ## Raises + + ### ValueError + + - switch_details is not set. """ method_name: str = inspect.stack()[0][3] @@ -194,11 +208,13 @@ def ip_address_to_serial_number(self, ip_address: str) -> str: def ok_to_delete_files(self, ip_address: str) -> bool: """ - ### Summary - - Return True if files can be deleted on the switch with ip_address. - - Return False otherwise. + # Summary + + - Return True if files can be deleted on the switch with ip_address. + - Return False otherwise. + + ## Raises - ### Raises None """ self.refresh_switch_details() @@ -274,14 +290,17 @@ def raise_exception(property_name: str) -> None: def commit(self) -> None: """ - ### Summary + # Summary + Send the payload to delete files. - ### Raises - - ``ValueError`` if: - - Mandatory parameters are not set. + ## Raises + + ### ValueError + + - Mandatory parameters are not set. - ### Notes + ## Notes - pylint: disable=no-member is needed due to the results property being dynamically created by the @Properties.add_results decorator. """ @@ -296,10 +315,12 @@ def commit(self) -> None: def delete_files(self) -> None: """ - ### Summary + # Summary + Delete files that have been added with add_files(). - ### Raises + ## Raises + None """ # pylint: disable=no-member @@ -322,17 +343,20 @@ def delete_files(self) -> None: def validate_prerequisites_for_add_file(self) -> None: """ - ### Summary + # Summary + Verify that mandatory prerequisites are met before calling add_file() - ### Raises - - ``ValueError`` if: - - ``filename`` is not set. - - ``filepath`` is not set. - - ``ip_address`` is not set. - - ``supervisor`` is not set. - - ``switch_details`` is not set. - - ``target`` is not set. + ## Raises + + ### ValueError + + - `filename` is not set. + - `filepath` is not set. + - `ip_address` is not set. + - `supervisor` is not set. + - `switch_details` is not set. + - `target` is not set. """ method_name: str = inspect.stack()[0][3] @@ -356,40 +380,45 @@ def raise_exception(property_name: str) -> None: def partition_and_serial_number_exist_in_payload(self) -> bool: """ - ### Summary - - Return True if the partition and serialNumber associated with the - file exist in the payload. - - Return False otherwise. + # Summary + + - Return True if the partition and serialNumber associated with the file exist in the payload. + - Return False otherwise. + + ## Raises - ### Raises None - ### payload Structure + ## payload Structure - "deleteFiles": [ - { - "files": [ - { - "bootflashType": "active", - "fileName": "bar.txt", - "filePath": "bootflash:" - } - ], - "partition": "bootflash:", - "serialNumber": "FOX2109PGCS" - }, - { - "files": [ - { - "bootflashType": "active", - "fileName": "black.txt", - "filePath": "bootflash:" - } - ], - "partition": "bootflash:", - "serialNumber": "FOX2109PGD0" - } - ] + ```json + { + "deleteFiles": [ + { + "files": [ + { + "bootflashType": "active", + "fileName": "bar.txt", + "filePath": "bootflash:" + } + ], + "partition": "bootflash:", + "serialNumber": "FOX2109PGCS" + }, + { + "files": [ + { + "bootflashType": "active", + "fileName": "black.txt", + "filePath": "bootflash:" + } + ], + "partition": "bootflash:", + "serialNumber": "FOX2109PGD0" + } + ] + } + ``` """ found: bool = False for item in self.payload["deleteFiles"]: @@ -409,10 +438,8 @@ def add_file_to_existing_payload(self) -> None: Add a file to the payload if the following are true: - - The serialNumber and partition associated with the file exist in - the payload. - - The file does not already exist in the files list for that - serialNumber and partition. + - The serialNumber and partition associated with the file exist in the payload. + - The file does not already exist in the files list for that serialNumber and partition. ## Raises @@ -420,7 +447,7 @@ def add_file_to_existing_payload(self) -> None: ## Details - We are looking at the following structure. + self.payload consists of the following structure. ```json { @@ -475,11 +502,13 @@ def add_file_to_existing_payload(self) -> None: def add_file_to_payload(self) -> None: """ - ### Summary + # Summary + Add a file to the payload if the serialNumber and partition do not yet exist in the payload. - ### Raises + ## Raises + None """ if not self.partition_and_serial_number_exist_in_payload(): @@ -500,12 +529,16 @@ def add_file_to_payload(self) -> None: def add_file(self) -> None: """ - ### Summary + # Summary + Add a file to the payload. - ### Raises - - ``ValueError`` if: - - The switch does not allow file deletion. + ## Raises + + ### ValueError + + - The switch does not allow file deletion. + - Mandatory parameters are not set. """ method_name: str = inspect.stack()[0][3] self.validate_prerequisites_for_add_file() @@ -521,17 +554,18 @@ def add_file(self) -> None: def update_diff(self) -> None: """ - ### Summary - Update ``diff`` with ``target``. + # Summary + + Update `diff` with `target`. + + ## Raises - ### Raises None - ### Notes - - ``target`` has already been validated to be set (not None) in - ``validate_prerequisites_for_add_file()``. - - ``target`` has already been validated to be a dictionary and to - contain ``ip_address`` in ``target.setter``. + ## Notes + + - `target` has already been validated to be set (not None) in `validate_prerequisites_for_add_file()`. + - `target` has already been validated to be a dictionary and to contain `ip_address` in `target.setter`. """ ip_address: str = self.target.get("ip_address", "") if ip_address not in self.diff: @@ -541,20 +575,24 @@ def update_diff(self) -> None: @property def filepath(self) -> str: """ - ### Summary - Return the current ``filepath``. + # Summary + + Return the current `filepath`. - ``filepath`` is the path to the file to be deleted. + `filepath` is the path to the file to be deleted. + + ## Raises - ### Raises None - ### Associated key - ``filePath`` + ## Associated key - ### Example values - - ``bootflash:`` - - ``bootflash:/mydir/mysubdir/`` + `filePath` + + ## Example values + + - `bootflash:` + - `bootflash:/mydir/mysubdir/` """ return self._filepath @@ -565,19 +603,22 @@ def filepath(self, value: str) -> None: @property def filename(self) -> str: """ - ### Summary - Return the current ``filename``. + # Summary - ``filename`` is the name of the file to be deleted. + Return the current `filename`. + + `filename` is the name of the file to be deleted. + + ## Raises - ### Raises None - ### Associated key - ``fileName`` + ## Associated key + `fileName` + + ## Example value - ### Example value - ``n9000-epld.10.2.5.M.img`` + `n9000-epld.10.2.5.M.img` """ return self._filename @@ -588,17 +629,21 @@ def filename(self, value: str) -> None: @property def ip_address(self) -> str: """ - ### Summary - The ip address of the switch on which ``filename`` resides. + # Summary + + The ip address of the switch on which `filename` resides. + + ## Raises - ### Raises None - ### Associated key - ``serialNumber`` (ip_address is converted to serialNumber) + ## Associated key + + `serialNumber` (ip_address is converted to serialNumber) - ### Example value - ``192.168.1.2`` + ## Example value + + `192.168.1.2` """ return self._ip_address @@ -609,17 +654,21 @@ def ip_address(self, value: str) -> None: @property def partition(self) -> str: """ - ### Summary - The partition on which ``filename`` resides. + # Summary + + The partition on which `filename` resides. + + ## Raises - ### Raises None - ### Associated key - ``partition`` + ## Associated key + + `partition` + + ## Example value - ### Example value - ``bootflash:`` + `bootflash:` """ return self._partition @@ -632,20 +681,18 @@ def rest_send(self) -> RestSend: """ # Summary - An instance of the RestSend class. + Set/get an instance of the RestSend class. ## Raises - - setter: `TypeError` if the value is not an instance of RestSend. - - setter: `ValueError` if RestSend.params is not set. + ### TypeError - ## getter + - setter: if the value is not an instance of RestSend. - Return an instance of the RestSend class. + ### ValueError - ## setter + - setter: if RestSend.params is not set. - Set an instance of the RestSend class. """ method_name: str = inspect.stack()[0][3] if not self._rest_send.params: @@ -676,19 +723,13 @@ def results(self) -> Results: """ # Summary - An instance of the Results class. + Set/get an instance of the Results class. ## Raises - - setter: `TypeError` if the value is not an instance of Results. - - ## getter - - Return an instance of the Results class. + ### TypeError - ## setter - - Set an instance of the Results class. + - setter: if the value is not an instance of Results. """ return self._results @@ -712,21 +753,24 @@ def results(self, value: Results) -> None: @property def supervisor(self) -> str: """ - ### Summary - Return the current ``supervisor``. + # Summary - ``supervisor`` is the switch supervisor card (active or standby) - on which ``filename`` resides. + Return the current `supervisor`. + + `supervisor` is the switch supervisor card (active or standby) on which `filename` resides. + + ## Raises - ### Raises None - ### Associated key - ``bootflashType`` + ## Associated key + + `bootflashType` - ### Example values - - ``active`` - - ``standby`` + ## Example values + + - `active` + - `standby` """ return self._supervisor @@ -737,12 +781,15 @@ def supervisor(self, value: str) -> None: @property def switch_details(self) -> SwitchDetails: """ - ### Summary + # Summary + An instance of the ``SwitchDetails()`` class. - ### Raises - - ``TypeError`` if ``switch_details`` is not an instance of - ``SwitchDetails``. + ## Raises + + ### TypeError + + - `switch_details`` is not an instance of `SwitchDetails`. """ return self._switch_details @@ -766,15 +813,26 @@ def switch_details(self, value): @property def target(self) -> dict[str, str]: """ - ### Summary - ``target`` is a dictionary that is used to set the diff passed to - Results. + # Summary + + `target` is a dictionary that is used to set the diff passed to Results. + + `target` is appended to a list of targets in `BootflashFiles().add_file()`, so must be + passed for each file to be deleted. See Usage example in the class docstring. - ``target`` is appended to a list of targets in - ``BootflashFiles().add_file()``, so must be passed for each file - to be deleted. See Usage example in the class docstring. + ## Raises + + ### TypeError + + - `target` is not a dictionary. + + ### ValueError + + - `target` is missing a mandatory key. + + + ## `target` Structure - ### ``target`` Structure ```json { "date": "2023-09-19 22:20:07", @@ -787,25 +845,21 @@ def target(self) -> dict[str, str]: } ``` - ### Raises - - ``TypeError`` if: - - ``target`` is not a dictionary. - - ``ValueError`` if: - - ``target`` is missing a mandatory key. + ## Associated key - ### Associated key None - ### Notes + ## Notes + 1. Since (at least with the dcnm_bootflash module) the user references switches using ip_address, and the NDFC bootflash-files payload includes only serialNumber, we - decided to use ``target`` as the diff since it contains the + decided to use `target` as the diff since it contains the ip_address and serial_number (as well as the size, date etc, which are potentially more useful than the info in the payload. - 2. ``BootflashFiles()`` requires that the ``ip_address`` key - be present in target, since it uses ``ip_address`` as the key + 2. `BootflashFiles()` requires that the `ip_address` key + be present in `target`, since it uses `ip_address` as the key for the diff. Of the other fields, we also require that filepath, serial_number and supervisor are present since they add value to the diff. The other fields shown above SHOULD be included diff --git a/plugins/module_utils/bootflash/bootflash_info.py b/plugins/module_utils/bootflash/bootflash_info.py index 035c65397..0fd1e3bf6 100644 --- a/plugins/module_utils/bootflash/bootflash_info.py +++ b/plugins/module_utils/bootflash/bootflash_info.py @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +Retrieve and filter bootflash contents. +""" from __future__ import absolute_import, division, print_function __metaclass__ = type # pylint: disable=invalid-name @@ -40,12 +43,15 @@ class BootflashInfo: ## Raises - - ``ValueError`` if: - - params is not set. - - switches is not set. - - ``TypeError`` if: - - switches is not a list. - - switches contains anything other than strings. + ### ValueError + + - params is not set. + - switches is not set. + + ### TypeError + + - switches is not a list. + - switches contains anything other than strings. ## Usage @@ -265,10 +271,11 @@ def validate_refresh_parameters(self) -> None: ## Raises - - `ValueError` if: - - self.rest_send.params is not set. - - self.switches is not set. - - switches is not set. + ### ValueError + + - self.rest_send.params is not set. + - self.switches is not set. + - switches is not set. """ method_name: str = inspect.stack()[0][3] @@ -293,8 +300,9 @@ def refresh(self) -> None: ## Raises - - `ValueError` if: - - switches is not set. + ### ValueError + + - switches is not set. """ self.validate_refresh_parameters() @@ -318,8 +326,9 @@ def refresh_bootflash_info(self) -> None: ## Raises - - `ValueError` if: - - serial_number cannot be found for a switch. + ### ValueError + + - serial_number cannot be found for a switch. """ method_name: str = inspect.stack()[0][3] self.info_dict = {} @@ -355,13 +364,13 @@ def validate_prerequisites_for_build_matches(self) -> None: """ # Summary - Verify that mandatory prerequisites are met before calling - `build_matches()`. + Verify that mandatory prerequisites are met before calling `build_matches()`. ## Raises - - `ValueError` if: - - info_dict is empty i.e. `refresh` has not been called. + ### ValueError + + - info_dict is empty i.e. `refresh` has not been called. """ method_name: str = inspect.stack()[0][3] @@ -373,10 +382,10 @@ def validate_prerequisites_for_build_matches(self) -> None: def match_filter_filepath(self, target: dict[str, str]) -> bool: """ - ## Summary + # Summary - - Return True if the target's `filepath` matches `filter_filepath`. - - Return False otherwise. + - Return True if the target's `filepath` matches `filter_filepath`. + - Return False otherwise. ## Raises @@ -394,8 +403,8 @@ def match_filter_supervisor(self, target: dict[str, str]) -> bool: """ # Summary - - Return True if the target's `bootflash_type` matches `filter_supervisor`. - - Return False otherwise. + - Return True if the target's `bootflash_type` matches `filter_supervisor`. + - Return False otherwise. ## Raises @@ -411,8 +420,8 @@ def match_filter_switch(self, target: dict[str, str]) -> bool: """ # Summary - - Return True if the target's `ip_address` matches `filter_switch`. - - Return False otherwise. + - Return True if the target's `ip_address` matches `filter_switch`. + - Return False otherwise. ## Raises @@ -478,8 +487,7 @@ def filter_filepath(self) -> str: Return the current `filter_filepath`. - `filter_filepath` is a file path used to filter the results - of the query. It can include file globbing. + `filter_filepath` is a file path used to filter the results of the query. It can include file globbing. ## Raises @@ -487,10 +495,10 @@ def filter_filepath(self) -> str: ## Examples - - All txt files in the bootflash directory - - instance.filter_filepath = "bootflash:/*.txt" - - All txt files on all flash devices - - instance.filter_filepath = "*:/*.txt" + - All txt files in the bootflash directory + - instance.filter_filepath = "bootflash:/*.txt" + - All txt files on all flash devices + - instance.filter_filepath = "*:/*.txt" """ return self._filter_filepath @@ -513,9 +521,9 @@ def filter_supervisor(self) -> str: ## Raises - - `ValueError` if: - - value is not one of the valid_supervisor values - "active" or "standby". + ### ValueError + + - `value` is not one of the valid_supervisor values "active" or "standby". ## Example @@ -604,20 +612,17 @@ def rest_send(self) -> RestSend: """ # Summary - An instance of the RestSend class. + Get/set an instance of the RestSend class. ## Raises - - setter: `TypeError` if the value is not an instance of RestSend. - - getter: `ValueError` if RestSend.params is not set. - - ## getter + ### TypeError - Return an instance of the RestSend class. + - setter: if the value is not an instance of RestSend. - ## setter + ### ValueError - Set an instance of the RestSend class. + - getter: if RestSend.params is not set. """ method_name: str = inspect.stack()[0][3] if not self._rest_send.params: @@ -648,19 +653,14 @@ def results(self) -> Results: """ # Summary - An instance of the Results class. + Get/set an instance of the Results class. ## Raises - - setter: `TypeError` if the value is not an instance of Results. - - ## getter - - Return an instance of the Results class. + ### TypeError - ## setter + - setter: if the value is not an instance of Results. - Set an instance of the Results class. """ return self._results @@ -690,7 +690,9 @@ def switch_details(self) -> SwitchDetails: ## Raises - - `TypeError` if `switch_details` is not an instance of `SwitchDetails()`. + ### TypeError + + - `switch_details` is not an instance of `SwitchDetails()`. """ return self._switch_details @@ -720,13 +722,14 @@ def switches(self) -> list[str]: ## Raises - - getter: None - - setter: - - `TypeError` if: - - switches is not a list. - - switches contains anything other than strings. - - `ValueError` if: - - switches list is empty. + ### TypeError + + - setter: switches is not a list. + - setter: switches contains anything other than strings. + + ### ValueError + + - setter: switches list is empty. ## Example diff --git a/plugins/module_utils/bootflash/convert_file_info_to_target.py b/plugins/module_utils/bootflash/convert_file_info_to_target.py index 0eb9d7208..7a03bf507 100644 --- a/plugins/module_utils/bootflash/convert_file_info_to_target.py +++ b/plugins/module_utils/bootflash/convert_file_info_to_target.py @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +Build a `target` dictionary from a `file_info` dictionary. +""" from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -24,12 +27,16 @@ class ConvertFileInfoToTarget: """ - ### Summary - Build a ``target`` dictionary from a ``file_info`` dictionary. + # Summary - ### Raises + Build a `target` dictionary from a `file_info` dictionary. + + ## Raises + + ## `file_info` structure + + Returned by the bootflash-info endpoint. - ### ``file_info`` Dictionary (from bootflash-info endpoint response) ```json { "bootflash_type": "active", @@ -44,7 +51,8 @@ class ConvertFileInfoToTarget: } ``` - ### ``target`` Dictionary + ## `target` structure + ```json { "date": "2023-09-19 22:20:07", @@ -57,7 +65,8 @@ class ConvertFileInfoToTarget: } ``` - ### Usage + ## Usage + ```python instance = ConvertFileInfoToTarget() instance.file_info = { @@ -75,7 +84,8 @@ class ConvertFileInfoToTarget: print(instance.target) ``` - ### Output + ## Output + ```json { "date": "2023-09-19 22:20:07", @@ -108,13 +118,15 @@ def __init__(self) -> None: def validate_commit_parameters(self) -> None: """ - ### Summary - Validate that the parameters required to build the target dictionary - are present. + # Summary + + Validate that the parameters required to build the target dictionary are present. - ### Raises - - ``ValueError`` if: - - ``file_info`` is not set. + ## Raises + + ### ValueError + + - `file_info` is not set. """ method_name = inspect.stack()[0][3] @@ -127,28 +139,34 @@ def raise_error(msg): def commit(self) -> None: """ - ### Summary - Given ``file_info``, which is the information for a single file from - the bootflash-info endpoint response, build a ``target`` dictionary + # Summary + + Given `file_info`, which is the information for a single file from + the bootflash-info endpoint response, build a `target` dictionary containing: - 1. A Posix path ``filepath`` from the ``file_info`` dictionary. - 2. Rename ``bootflash_type`` to ``supervisor`` in the target + 1. A Posix path `filepath` from the `file_info` dictionary. + 2. Rename `bootflash_type` to `supervisor` in the target dictionary. - 3. Convert the ``date`` value to a more easily digestable format + 3. Convert the `date` value to a more easily digestable format (YYYY-MM-DD HH:MM:SS). - 4. Rename ipAddr to ip_address and strip the leading space that + 4. Rename `ipAddr` to `ip_address` and strip the leading space that NDFC adds. - 5. Rename serialNumber to serial_number and add to the target + 5. Rename `serialNumber` to `serial_number` and add to the target dictionary. - 6. Add size to the target dictionary. + 6. Add `size` to the target dictionary. + + ## Raises + + ### ValueError + + - `file_info` is not set. + - `target` cannot be built from `file_info`. + + ## `file_info` structure - ### Raises - - ``ValueError`` if: - - ``file_info`` is not set. - - ``target`` cannot be built from ``file_info``. + From bootflash-info endpoint response. - ### ``file_info`` (from bootflash-info endpoint response) ```json { "bootflash_type": "active", @@ -163,7 +181,8 @@ def commit(self) -> None: } ``` - ### ``target`` Structure + ## `target` structure + ```json { "date": "2023-09-19 22:20:07", @@ -215,13 +234,16 @@ def raise_error(msg): def _get(self, key): """ - ### Summary - Get the value of a key from the ``file_info`` dictionary. + # Summary + + Get the value of a key from the `file_info` dictionary. + + ## Raises + + ### ValueError - ### Raises - - ``ValueError`` if: - - ``file_info`` has not been set before calling _get. - - ``key`` is not in the target dictionary. + - `file_info` has not been set before calling _get. + - `key` is not in the target dictionary. """ method_name = inspect.stack()[0][3] @@ -241,21 +263,27 @@ def raise_error(msg): @property def file_info(self): """ - ### Summary + # Summary + A single file dictionary from the bootflash-info endpoint response. - ### Raises - - ``ValueError`` if: - - ``file_info`` is not a dictionary. - - ``file_info`` does not contain the requisite keys. + ## Raises + + ### ValueError + + - `file_info` is not a dictionary. + - `file_info` does not contain the requisite keys. + + ## Expected Structure - ### Expected Structure This class uses the following keys from the file_info dictionary: - - fileName - - filePath - - bootflash_type - ### Example + - fileName + - filePath + - bootflash_type + + ## Example + ```json { "bootflash_type": "active", @@ -279,16 +307,19 @@ def file_info(self, value): @property def date(self): """ - ### Summary - The value of ``date`` from the ``file_info`` dictionary - converted to a ``datetime`` object. The string representation of - this object will be "YYYY-MM-DD HH:MM:SS". - - ### Raises - - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``date`` is not in the ``file_info`` dictionary. - - ``date`` cannot be converted to a datetime object. + # Summary + + The value of `date` from the `file_info` dictionary converted to a `datetime` object. + + The string representation of this object will be "YYYY-MM-DD HH:MM:SS". + + ## Raises + + ### ValueError + + - `file_info` has not been set before accessing. + - `date` is not in the `file_info` dictionary. + - `date` cannot be converted to a datetime object. """ method_name = inspect.stack()[0][3] try: @@ -304,103 +335,127 @@ def date(self): @property def device_name(self): """ - ### Summary - The value of ``deviceName`` from the ``file_info`` dictionary. + # Summary - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``deviceName`` is not in the ``file_info`` dictionary. + The value of `deviceName` from the `file_info` dictionary. + + ## Raises + + ### ValueError + + - `file_info` has not been set before accessing. + - `deviceName` is not in the `file_info` dictionary. """ return self._get("deviceName") @property def filename(self): """ - ### Summary - The value of ``fileName`` from the ``file_info`` dictionary. + # Summary + + The value of `fileName` from the `file_info` dictionary. - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``fileName`` is not in the ``file_info`` dictionary. + ## Raises + + ### ValueError + + - `file_info` has not been set before accessing. + - `fileName` is not in the `file_info` dictionary. """ return self._get("fileName") @property def filepath(self): """ - ### Summary - The value of ``filePath`` from the ``file_info`` dictionary. + # Summary + + The value of `filePath` from the `file_info` dictionary. - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``filePath`` is not in the ``file_info`` dictionary. + ## Raises + + ### ValueError + + - `file_info` has not been set before accessing. + - `filePath` is not in the `file_info` dictionary. """ return self._get("filePath") @property def ip_address(self): """ - ### Summary - The stripped value of ``ipAddr`` from the ``file_info`` dictionary. + # Summary + + The stripped value of `ipAddr` from the `file_info` dictionary. + + ## Raises - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``ipAddr`` is not in the ``file_info`` dictionary. + ### ValueError + + - `file_info` has not been set before accessing. + - `ipAddr` is not in the `file_info` dictionary. """ return self._get("ipAddr").strip() @property def name(self): """ - ### Summary - The value of ``name`` from the ``file_info`` dictionary. + # Summary + + The value of `name` from the `file_info` dictionary. + + ## Raises - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``name`` is not in the ``file_info`` dictionary. + ### ValueError + + - `file_info` has not been set before accessing. + - `name` is not in the `file_info` dictionary. """ return self._get("name") @property def serial_number(self): """ - ### Summary - The value of ``serialNumber`` from the ``file_info`` dictionary. + # Summary + + The value of `serialNumber` from the `file_info` dictionary. + + ## Raises + + ### ValueError - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``serialNumber`` is not in the ``file_info`` dictionary. + - `file_info` has not been set before accessing. + - `serialNumber` is not in the `file_info` dictionary. """ return self._get("serialNumber") @property def size(self): """ - ### Summary - The value of ``size`` from the ``file_info`` dictionary. + # Summary + + The value of `size` from the `file_info` dictionary. + + ## Raises + + ### ValueError - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``size`` is not in the ``file_info`` dictionary. + - `file_info` has not been set before accessing. + - `size` is not in the `file_info` dictionary. """ return self._get("size") @property def target(self): """ - ### Summary - The target dictionary built from the ``file_info`` dictionary. + # Summary - ### Raises - ``ValueError`` if: - - ``commit()`` has not been called before accessing. + The target dictionary built from the `file_info` dictionary. + + ## Raises + + ### ValueError + + - `commit()` has not been called before accessing. """ if self._target is None: msg = f"{self.class_name}.target: " @@ -415,12 +470,15 @@ def target(self, value): @property def supervisor(self): """ - ### Summary - The value of ``bootflash_type`` from the ``file_info`` dictionary. + # Summary + + The value of `bootflash_type` from the `file_info` dictionary. + + ## Raises + + ### ValueError - ### Raises - ``ValueError`` if: - - ``file_info`` has not been set before accessing. - - ``bootflash_type`` is not in the ``file_info`` dictionary. + - `file_info` has not been set before accessing. + - `bootflash_type` is not in the `file_info` dictionary. """ return self._get("bootflash_type") diff --git a/plugins/module_utils/bootflash/convert_target_to_params.py b/plugins/module_utils/bootflash/convert_target_to_params.py index fad8abeab..b25ee55e1 100644 --- a/plugins/module_utils/bootflash/convert_target_to_params.py +++ b/plugins/module_utils/bootflash/convert_target_to_params.py @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +Parse `target` into its consituent API parameters +""" from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -22,17 +25,22 @@ class ConvertTargetToParams: """ - ### Summary - Parse ``target`` into its consituent API parameters. + # Summary - ### Raises - - ``ValueError`` if: - - ``filepath`` is not set in the target dict. - - ``supervisor`` is not set in the target dict. + Parse `target` into its consituent API parameters. + + ## Raises + + ### ValueError + + - `filepath` is not set in the target dict. + - `supervisor` is not set in the target dict. + + ## Usage + + ### Example 1, file in directory. - ### Usage ```python - # Example 1, file in directory. target = { "filepath": "bootflash:/myDir/foo.txt", "supervisor": "active" @@ -44,19 +52,22 @@ class ConvertTargetToParams: print(instance.filepath) # bootflash:/myDir/ print(instance.filename) # foo.txt print(instance.supervisor) # active + ``` + + ### Example 2, file in root of bootflash partition. - # Example 2, file in root of bootflash partition. + ```python target = { "filepath": "bootflash:/foo.txt", "supervisor": "active" } + instance = ConvertTargetToParams() instance.target = target instance.commit() print(instance.partition) # bootflash: print(instance.filepath) # bootflash: print(instance.filename) # foo.txt print(instance.supervisor) # active - ``` """ @@ -78,12 +89,15 @@ def __init__(self) -> None: def commit(self): """ - ### Summary + # Summary + Commit the target to be parsed. - ### Raises - - ``ValueError`` if: - - target is not set before calling commit. + ## Raises + + ### ValueError + + - `target` is not set before calling commit. """ if self.target is None: msg = f"{self.class_name}.commit: " @@ -95,19 +109,27 @@ def commit(self): def parse_target(self) -> None: """ - ### Summary - Parse target into its consituent API parameters. + # Summary + + Parse `target` into its consituent API parameters. + + ## Raises + + ### ValueError + + - `filepath` is not set in the target dict. + - `supervisor` is not set in the target dict. - ### Raises - - ``ValueError`` if: - - ``filepath`` is not set in the target dict. - - ``supervisor`` is not set in the target dict. + ## Target Structure - ### Target Structure + ```json { filepath: bootflash:/myDir/foo.txt supervisor: active } + ``` + + ## API Parameters Set the following API parameters from the above structure: @@ -116,7 +138,8 @@ def parse_target(self) -> None: - self.filename: foo.txt - self.supervisor: active - ### Notes + ## Notes + - While this method is written to support files in directories, the NDFC API does not support listing files within a directory. Hence, we currently support only files in the root directory of the @@ -166,12 +189,15 @@ def raise_error(msg): @property def filename(self): """ - ### Summary - Return the filename parsed from ``target``. + # Summary + + Return the filename parsed from `target`. - ### Raises - ``ValueError`` if: - - ``commit()`` has not been called before accessing this property. + ## Raises + + ### ValueError + + - `commit()` has not been called before accessing this property. """ method_name = inspect.stack()[0][3] if not self.committed: @@ -187,12 +213,15 @@ def filename(self, value): @property def filepath(self): """ - ### Summary - Return the filepath parsed from ``target``. + # Summary + + Return the filepath parsed from `target`. + + ## Raises - ### Raises - ``ValueError`` if: - - ``commit()`` has not been called before accessing this property. + ### ValueError + + - `commit()` has not been called before accessing this property. """ method_name = inspect.stack()[0][3] if not self.committed: @@ -208,9 +237,9 @@ def filepath(self, value): @property def target(self): """ - ### Summary - The target to be parsed. This is a dictionary with the following - structure: + # Summary + + The target to be parsed. This is a dictionary with the following structure: ```json { @@ -228,12 +257,15 @@ def target(self, value): @property def partition(self): """ - ### Summary - Return the partition parsed from ``target``. + # Summary + + Return the partition parsed from `target`. + + ## Raises - ### Raises - ``ValueError`` if: - - ``commit()`` has not been called before accessing this property. + ### ValueError + + - `commit()` has not been called before accessing this property. """ method_name = inspect.stack()[0][3] if not self.committed: @@ -255,14 +287,17 @@ def partition(self, value): @property def supervisor(self): """ - ### Summary - Return the supervisor parsed from ``target``. This is the state + # Summary + + Return the supervisor parsed from `target`. This is the state (active or standby) of the supervisor that hosts the file described - in ``target``. + in `target`. + + ## Raises + + ### ValueError - ### Raises - ``ValueError`` if: - - ``commit()`` has not been called before accessing this property. + - `commit()` has not been called before accessing this property. """ method_name = inspect.stack()[0][3] if not self.committed: diff --git a/plugins/modules/dcnm_bootflash.py b/plugins/modules/dcnm_bootflash.py index e3deac445..3f696e3bf 100644 --- a/plugins/modules/dcnm_bootflash.py +++ b/plugins/modules/dcnm_bootflash.py @@ -13,6 +13,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +Bootflash management for Nexus switches. +""" # pylint: disable=wrong-import-position from __future__ import absolute_import, division, print_function @@ -293,10 +296,10 @@ def raise_error(msg): def get_want(self) -> None: """ - ### Summary + # Summary + 1. Validate the playbook configs - 2. Convert the validated configs to the structure required by the - the Delete() and Query() classes. + 2. Convert the validated configs to the structure required by the the Delete() and Query() classes. 3. Update self.want with this list of payloads If a switch in the switches list does not have a targets key, add the @@ -304,22 +307,28 @@ def get_want(self) -> None: playbook. Else, use the switch's targets info (i.e. the switch's targets info overrides the global targets info). - ### Raises - - ValueError if: - - ``ip_address`` is missing from a switch dict. - - ``filepath`` is missing from a target dict. - - TypeError if: - - The value of ``targets`` is not a list of dictionaries. - - ### ``want`` Structure - - A list of dictionaries. Each dictionary contains the following keys: - - ip_address: The ip address of the switch. - - targets: A list of dictionaries. Each dictionary contains the - following keys: - - filepath: The path to the file to be deleted or queried. - - supervisor: The supervisor containing the filepath. - - ### Example ``want`` Structure + ## Raises + + ### ValueError + + - `ip_address` is missing from a switch dict. + - `filepath` is missing from a target dict. + + ### TypeError + + - The value of `targets` is not a list of dictionaries. + + ## ``want`` structure + + A list of dictionaries. Each dictionary contains the following keys: + + - ip_address: The ip address of the switch. + - targets: A list of dictionaries. Each dictionary contains the following keys: + - filepath: The path to the file to be deleted or queried. + - supervisor: The supervisor containing the filepath. + + ## Example ``want`` structure + ```json [ { @@ -377,18 +386,20 @@ def raise_type_error(msg): @property def rest_send(self) -> RestSend: """ - ### Summary - An instance of the RestSend class. + # Summary + + Get/set an instance of the RestSend class. + + ## Raises + + ### ValueError + + - getter: `rest_send` has not been properly initialized (missing params). - ### Raises - - getter: `ValueError` if rest_send has not been properly initialized (missing params). - - setter: ``TypeError`` if the value is not an instance of RestSend. + ### TypeError - ### getter - Return a properly initialized instance of the RestSend class. + - setter: The value is not an instance of `RestSend`. - ### setter - Set an instance of the RestSend class. """ if not self._rest_send.params: msg = f"{self.class_name}.rest_send: " @@ -416,12 +427,15 @@ def rest_send(self, value: RestSend): class Deleted(Common): """ - ### Summary + # Summary + Handle deleted state - ### Raises - - ValueError if: - - ``Common.__init__()`` raises TypeError or ValueError. + ## Raises + + ### ValueError + + - `Common.__init__()` raises TypeError or ValueError. """ def __init__(self, params: dict[str, Any]) -> None: @@ -445,22 +459,27 @@ def __init__(self, params: dict[str, Any]) -> None: def populate_files_to_delete(self, switch) -> None: """ - ### Summary - Populate the ``files_to_delete`` dictionary with files - the user intends to delete. - - ### Raises - - ``ValueError`` if: - - ``supervisor`` is not one of: - - active - - standby - - ### ``files_to_delete`` Structure - files_to_delete is a dictionary containing - - key: switch ip address. - - value: a list of dictionaries containing the files to delete. - - ### ``files_to_delete`` Example + # Summary + + Populate the `files_to_delete` dictionary with files the user intends to delete. + + ## Raises + + ### ValueError + + - `supervisor` is not one of + - active + - standby + + ## `files_to_delete` structure + + `files_to_delete` is a dictionary containing + + - key: switch ip address. + - value: a list of dictionaries containing the files to delete. + + ### Example + ```json { "172.22.150.112": [ @@ -495,15 +514,20 @@ def populate_files_to_delete(self, switch) -> None: def update_bootflash_files(self, ip_address: str, target: dict[str, str]) -> None: """ - ### Summary - Call ``BootflashFiles().add_file()`` to add the file associated with - ``ip_address`` and ``target`` to the list of files to be deleted. - - ### Raises - - ``TypeError`` if: - - ``target`` is not a dictionary. - - ``ValueError`` if: - - ``BootflashFiles().add_file`` raises ``ValueError``. + # Summary + + Call `BootflashFiles().add_file()` to add the file associated with + `ip_address` and `target` to the list of files to be deleted. + + ## Raises + + ### TypeError + + - `target` is not a dictionary. + + ### ValueError + + - `BootflashFiles().add_file` raises `ValueError`. """ method_name: str = inspect.stack()[0][3] @@ -543,16 +567,18 @@ def update_bootflash_files(self, ip_address: str, target: dict[str, str]) -> Non def commit(self) -> None: """ - ### Summary + # Summary + Delete the specified files if they exist. - ### Raises + # Raises + None. While this method does not directly raise exceptions, it calls other methods that may raise the following exceptions: - - ControllerResponseError - - TypeError - - ValueError + - ControllerResponseError + - TypeError + - ValueError """ # Populate self.switches self.get_want() @@ -594,12 +620,15 @@ def commit(self) -> None: class Query(Common): """ - ### Summary + # Summary + Handle query state. - ### Raises - - ValueError if: - - ``Common.__init__()`` raises TypeError or ValueError. + ## Raises + + ### ValueError + + -`Common.__init__()` raises TypeError or ValueError. """ def __init__(self, params: dict[str, Any]) -> None: @@ -625,10 +654,12 @@ def __init__(self, params: dict[str, Any]) -> None: def register_null_result(self) -> None: """ - ### Summary + # Summary + Register a null result when there are no switches to query. - ### Raises + ## Raises + None """ response_dict: dict[str, dict[str, Any]] = {} @@ -657,9 +688,9 @@ def commit(self) -> None: None. While this method does not directly raise exceptions, it calls other methods that may raise the following exceptions: - - ControllerResponseError - - TypeError - - ValueError + - ControllerResponseError + - TypeError + - ValueError """ method_name: str = inspect.stack()[0][3] From 98ddba9414f015fc9451804473c2e6f5cc9561f9 Mon Sep 17 00:00:00 2001 From: Allen Robel Date: Tue, 2 Dec 2025 10:24:07 -1000 Subject: [PATCH 2/3] Appease black linter No functional changes. --- plugins/module_utils/bootflash/convert_file_info_to_target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/bootflash/convert_file_info_to_target.py b/plugins/module_utils/bootflash/convert_file_info_to_target.py index 7a03bf507..5997dc79d 100644 --- a/plugins/module_utils/bootflash/convert_file_info_to_target.py +++ b/plugins/module_utils/bootflash/convert_file_info_to_target.py @@ -310,7 +310,7 @@ def date(self): # Summary The value of `date` from the `file_info` dictionary converted to a `datetime` object. - + The string representation of this object will be "YYYY-MM-DD HH:MM:SS". ## Raises From 3e12383aaa60f861063834404e373a5326ce716d Mon Sep 17 00:00:00 2001 From: Allen Robel Date: Tue, 2 Dec 2025 10:42:19 -1000 Subject: [PATCH 3/3] Address Copilot comments No functional changes in this commit. 1. Fix typos 2. Remove extra backtick 3. Remove extra blank line 4. Minor reformatting of docstrings --- plugins/module_utils/bootflash/bootflash_files.py | 5 ++--- .../bootflash/convert_file_info_to_target.py | 14 +++++--------- .../bootflash/convert_target_to_params.py | 6 +++--- plugins/modules/dcnm_bootflash.py | 4 ++-- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/plugins/module_utils/bootflash/bootflash_files.py b/plugins/module_utils/bootflash/bootflash_files.py index c5d9ca8a7..6143a48e4 100644 --- a/plugins/module_utils/bootflash/bootflash_files.py +++ b/plugins/module_utils/bootflash/bootflash_files.py @@ -538,7 +538,7 @@ def add_file(self) -> None: ### ValueError - The switch does not allow file deletion. - - Mandatory parameters are not set. + - Mandatory parameters are not set (see `validate_prerequisites_for_add_file()`). """ method_name: str = inspect.stack()[0][3] self.validate_prerequisites_for_add_file() @@ -789,7 +789,7 @@ def switch_details(self) -> SwitchDetails: ### TypeError - - `switch_details`` is not an instance of `SwitchDetails`. + - `switch_details` is not an instance of `SwitchDetails`. """ return self._switch_details @@ -830,7 +830,6 @@ def target(self) -> dict[str, str]: - `target` is missing a mandatory key. - ## `target` Structure ```json diff --git a/plugins/module_utils/bootflash/convert_file_info_to_target.py b/plugins/module_utils/bootflash/convert_file_info_to_target.py index 5997dc79d..3b67c19f0 100644 --- a/plugins/module_utils/bootflash/convert_file_info_to_target.py +++ b/plugins/module_utils/bootflash/convert_file_info_to_target.py @@ -146,15 +146,11 @@ def commit(self) -> None: containing: 1. A Posix path `filepath` from the `file_info` dictionary. - 2. Rename `bootflash_type` to `supervisor` in the target - dictionary. - 3. Convert the `date` value to a more easily digestable format - (YYYY-MM-DD HH:MM:SS). - 4. Rename `ipAddr` to `ip_address` and strip the leading space that - NDFC adds. - 5. Rename `serialNumber` to `serial_number` and add to the target - dictionary. - 6. Add `size` to the target dictionary. + 2. Rename `bootflash_type` to `supervisor` in the `target` dictionary. + 3. Convert the `date` value to a more easily digestible format (YYYY-MM-DD HH:MM:SS). + 4. Rename `ipAddr` to `ip_address` and strip the leading space that NDFC adds. + 5. Rename `serialNumber` to `serial_number` and add to the `target` dictionary. + 6. Add `size` to the `target` dictionary. ## Raises diff --git a/plugins/module_utils/bootflash/convert_target_to_params.py b/plugins/module_utils/bootflash/convert_target_to_params.py index b25ee55e1..c19fc39b5 100644 --- a/plugins/module_utils/bootflash/convert_target_to_params.py +++ b/plugins/module_utils/bootflash/convert_target_to_params.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Parse `target` into its consituent API parameters +Parse `target` into its constituent API parameters """ from __future__ import absolute_import, division, print_function @@ -27,7 +27,7 @@ class ConvertTargetToParams: """ # Summary - Parse `target` into its consituent API parameters. + Parse `target` into its constituent API parameters. ## Raises @@ -111,7 +111,7 @@ def parse_target(self) -> None: """ # Summary - Parse `target` into its consituent API parameters. + Parse `target` into its constituent API parameters. ## Raises diff --git a/plugins/modules/dcnm_bootflash.py b/plugins/modules/dcnm_bootflash.py index 3f696e3bf..96f5256e3 100644 --- a/plugins/modules/dcnm_bootflash.py +++ b/plugins/modules/dcnm_bootflash.py @@ -571,7 +571,7 @@ def commit(self) -> None: Delete the specified files if they exist. - # Raises + ## Raises None. While this method does not directly raise exceptions, it calls other methods that may raise the following exceptions: @@ -628,7 +628,7 @@ class Query(Common): ### ValueError - -`Common.__init__()` raises TypeError or ValueError. + - `Common.__init__()` raises TypeError or ValueError. """ def __init__(self, params: dict[str, Any]) -> None: