Kane610 / aiounifi

Asynchronous library to communicate with Unifi Controller
MIT License
59 stars 51 forks source link

Add traffic routes #529

Closed ViViDboarder closed 9 months ago

ViViDboarder commented 9 months ago

Adds support for reading, enabling, disabling, as well as generally updating Traffic Routes.

I based this off the PR #409

One thing I wasn't sure about was what fields I should make properties vs leaving as accessible via .raw. I made a few of the ones that I plan to use, but I'm happy to just make them all accessible that way.

ViViDboarder commented 9 months ago

Wow. Funny timing. I wrote this yesterday and sat on pushing out the branch for PR, but now I see someone did the same in #528. Take your pick, I guess!

One notable difference would be that I added a few more types for Domain and MatchingTarget, making it a bit easier to work with, in my opinion. I also changed from toggle or enable/disable to save because it does allow arbitrary changes.

I wrote this because I use a custom DNS server so I cannot use the built in "Domain" routing rules. Instead I have to use IP addresses. I did find that even if you're using a different matching target, the values saved for the unused ones are still persisted. This let me write a script that would read my rules, look for IP matching rules that have stored domains, resolve them to IP addresses (using my own DNS server) and then write them back.

        await controller.traffic_routes.update()
        for item in controller.traffic_routes.values():
            if item.domains and item.matching_target == MatchingTarget.IP:
                print(item.description)

                # Look up unique ip addresses
                addresses = set()
                for domain in item.domains:
                    print(domain["domain"])
                    addresses.update(
                        result[4][0]
                        for result in socket.getaddrinfo(
                            domain["domain"],
                            80,
                            type=socket.SOCK_STREAM,
                            proto=socket.IPPROTO_IP,
                        )
                    )

                # Sort addresses by type and value
                sorted_addresses = list(
                    sorted(
                        addresses, key=lambda a: f"{'ip4' if '.' in a else 'ip6'}-{a}"
                    )
                )
                print(sorted_addresses)

                # Build addresses objects
                ip_addresses = [
                    IPAddress(
                        ip_or_subnet=a,
                        ip_version="v4" if "." in a else "v6",
                        port_ranges=[],
                        ports=[],
                    )
                    for a in sorted_addresses
                ]

                # Check for change
                if ip_addresses == item.raw["ip_addresses"]:
                    print("already up to date")
                    continue

                # Update while preserving current state
                item.raw["ip_addresses"] = ip_addresses
                result = await controller.traffic_routes.save(item)
Kane610 commented 9 months ago

Thank you very much! I merged the other PR, but feel free to rebase and add your changes on top. It felt right to give Alan the initial commit. I'm aim at doing a release of the library tomorrow night

ViViDboarder commented 9 months ago

@Kane610 I rebased into a new branch and PR #537, so I'll close this one.