#!/usr/bin/python # Copyright (c) 2024, Max Maxopoly # # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import annotations DOCUMENTATION = r""" module: usb_facts short_description: Allows listing information about USB devices version_added: 8.5.0 description: - Allows retrieving information about available USB devices through C(lsusb). author: - Max Maxopoly (@maxopoly) extends_documentation_fragment: - community.general.attributes - community.general.attributes.facts - community.general.attributes.facts_module requirements: - lsusb binary on PATH (usually installed through the package usbutils and preinstalled on many systems) """ EXAMPLES = r""" - name: Get information about USB devices community.general.usb_facts: - name: Print information about USB devices ansible.builtin.debug: msg: "On bus {{ item.bus }} device {{ item.device }} with id {{ item.id }} is {{ item.name }}" loop: "{{ ansible_facts.usb_devices }}" """ RETURN = r""" ansible_facts: description: Dictionary containing details of connected USB devices. returned: always type: dict contains: usb_devices: description: A list of USB devices available. returned: always type: list elements: dict contains: bus: description: The bus the usb device is connected to. returned: always type: str sample: "001" device: description: The device number occupied on the bus. returned: always type: str sample: "002" id: description: ID of the USB device. returned: always type: str sample: "1d6b:0002" name: description: Human readable name of the device. returned: always type: str sample: Linux Foundation 2.0 root hub """ import re from ansible.module_utils.basic import AnsibleModule def parse_lsusb(module, lsusb_path): rc, stdout, stderr = module.run_command(lsusb_path, check_rc=True) regex = re.compile(r"^Bus (\d{3}) Device (\d{3}): ID ([0-9a-f]{4}:[0-9a-f]{4}) (.*)$") usb_devices = [] for line in stdout.splitlines(): match = re.match(regex, line) if not match: module.fail_json(msg=f"failed to parse unknown lsusb output {line}", stdout=stdout, stderr=stderr) current_device = {"bus": match.group(1), "device": match.group(2), "id": match.group(3), "name": match.group(4)} usb_devices.append(current_device) return_value = {"usb_devices": usb_devices} module.exit_json( msg=f"parsed {len(usb_devices)} USB devices", stdout=stdout, stderr=stderr, ansible_facts=return_value ) def main(): module = AnsibleModule({}, supports_check_mode=True) # Set LANG env since we parse stdout module.run_command_environ_update = dict(LANGUAGE="C", LC_ALL="C") lsusb_path = module.get_bin_path("lsusb", required=True) parse_lsusb(module, lsusb_path) if __name__ == "__main__": main()