aruba / pyaoscx

Python modules for AOS-CX
Apache License 2.0
35 stars 24 forks source link

Get Interface of all MAC addresses #18

Closed martbhell closed 2 years ago

martbhell commented 2 years ago

Hello,

apologies if this is not the right location.

Using python 3.8 and latest pyaoscx in pip

I've been trying to use this library to get all the VLANs, their MAC addresses and which interface each MAC address is on.

https://github.com/aruba/pyaoscx/blob/master/pyaoscx/mac.py#L142 has a bit about port is under get() - but it's not under get_all() so I'm thinking maybe it's not possible?

Do you have some suggestion for how one could get this piece of information with one API call? (My workaround right now is to make lots of API calls, one for each Mac address directly the REST API without using this library to get the information). Do you happen to know if there some way to run a command like show mac-address-table ?

Could I throw in the depth= parameter in some good place?

I created a gist with an example code that tries to print the port but it doesn't, it fails with

$ python example_mac.py 
<class 'pyaoscx.mac.Mac'>
['_Mac__modified', '__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_extract_missing_parameters_from', '_get_and_copy_data', '_get_data', '_is_replace_required', '_mac_path', '_original_attributes', '_parent_vlan', '_post_data', '_put_data', '_send_data', '_set_configuration_items', '_set_vlan', '_uri', 'apply', 'base_uri', 'config_attrs', 'connected', 'create', 'delete', 'deprecated', 'display_name', 'from_id', 'from_response', 'from_uri', 'get', 'get_all', 'get_info', 'get_info_format', 'get_uri', 'indices', 'info_format', 'mac_address', 'mac_format', 'materialized', 'modified', 'path', 'resource_uri_name', 'session', 'update', 'uri_path', 'was_modified']
Ran into exception: 'Mac' object has no attribute 'port'. Closing session.
Traceback (most recent call last):
  File "example_mac.py", line 46, in get_aruba_macs
    print(macs[mac].port)
AttributeError: 'Mac' object has no attribute 'port'

https://gist.github.com/martbhell/ffa751efd09f156bc6eb3d4ce7890460

ajavier commented 2 years ago

Mac.get_all() should retrieve a dictionary with all the Macs in the system, keyed by the Mac index ("type,eui"). Also, note that get_all() calls from_uri() to instantiate the Mac, but it hasn't been materialized. You'll have to call get() to retrieve the internal data of the entry (that's why the port is not there).

This should work:

macs = Mac.get_all()
for idx,mac in macs.items():
    mac.get()
    print(idx, mac.port)
martbhell commented 2 years ago

Hello,

thanks for your time :)

Mac.get_all() should retrieve a dictionary with all the Macs in the system,

But it wants a parent_vlan 1 ; so I'm thinking it will only get MACs of one VLANs?

It feels like I'm still missing something, still don't get any Mac objects with port attribute even after calling mac.get() before trying to get mac.port

        allvlans = Vlan.get_all(sessio)
        for vlan in allvlans:
            vlanobject = Vlan(sessio, vlan)
            macs = Mac.get_all(session=sessio, parent_vlan=vlanobject)
            for idx,mac in macs.items():
                macget = mac.get()
                mac.get()
                mac.port
ajavier commented 2 years ago

You don't need to retrieve all the VLANs, just use the parent_vlan argument when retrieving the MACs:

    vlan_id = 1 # This is the VLAN you're interested in
    vlan = VLAN(session, vlan_id) # Create an unmaterialized VLAN
    macs = MAC.get_all(session, vlan)
    for idx,mac in macs.items();
        mac.get() # Materialize the object from the device
        print("%s -> %s" % (idx, mac))

It should really work. I don't see any reason why this is failing. Please, make sure to trace the log messages to validate that everything works as expected.

martbhell commented 2 years ago

Gotcha, I just looped over the VLANs because I didn't want to hard code in the VLAN ID. But for figuring this out good idea to reduce :)

Unfortunately using a single VLAN did not work either (fixed some minor things that looked like typos? in your reply (VLAN vs Vlan etc).

Below I kept some notes. But to me it looks like the extra payload like &selector=writable that's being sent to the /macs/ endpoint is making the endpoint return an empty string (200 2).

In my direct urllib calls to "/macs/dynamic,MAC" I haven't set depth= and selector= at all. If I set ?selector=writable it also returns "HTTP 200 2".

I tried to see if there was a way in _get_data to remove the selector but that led me down a rabbit hole I think.

But setting payload = {} on objects with attribute mac_address resulted in a 404. I'm confused :)


I also made sure that this wasn't some issue with a specific MAC address and had it continue the loop over all MAC addresses. None of them had the port attribute.

I've used the REST API version 10.08 and 10.04 same issue. Tried with pyaoscx 2.2.0 and 2.1.0 - did not print there either.


from pyaoscx.session import Session
from pyaoscx.vlan import Vlan
from pyaoscx.mac import Mac

switch_name = "CX6100.example.org"

sessio = Session(switch_name, "10.08")
password = "censored"

sessio.open("read-only-user-censored", password)

vlan_id = 1 # This is the VLAN you're interested in
vlan = Vlan(sessio, vlan_id) # Create an unmaterialized VLAN
macs = Mac.get_all(sessio, vlan)
for idx,mac in macs.items():
    mac.get() # Materialize the object from the device
    print("%s -> %s" % (idx, mac))
    print("%s -> %s" % (idx, mac.port))

Prints

{'dynamic,00%3AAA%3ABB%3ACC%3ADD%3AEE': <pyaoscx.mac.Mac object at 0x7f3624f26100>, 'dynamic,1c... }
dynamic,00%3AAA%3ABB%3ACC%3ADD%3AEE -> 00:AA:BB:CC:DD:EE
Ran into exception: 'Mac' object has no attribute 'port'. Closing session.
Traceback (most recent call last):
  File "example_mac.py", line 45, in get_aruba_macs
    print("%s -> %s" % (idx, mac.port))
AttributeError: 'Mac' object has no attribute 'port'

Thanks for the idea of using the logging!

DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): cx6100.example.org:443
DEBUG:urllib3.connectionpool:https://cx6100.example.org:443 "POST /rest/v10.08/login HTTP/1.1" 200 0
INFO:root:SUCCESS: Login succeeded
INFO:root:Retrieving all Mac data from switch
DEBUG:urllib3.connectionpool:https://cx6100.example.org:443 "GET /rest/v10.08/system/vlans/1/macs HTTP/1.1" 200 1458
INFO:root:Retrieving 00:AA:BB:CC:DD:EE from switch
DEBUG:urllib3.connectionpool:https://cx6100.example.org:443 "GET /rest/v10.08/system/vlans/1/macs/dynamic,,00%3AAA%3ABB%3ACC%3ADD%3AEE?depth=1&selector=writable HTTP/1.1" 200 2
DEBUG:urllib3.connectionpool:https://cx6100.example.org:443 "POST /rest/v10.08/logout HTTP/1.1" 200 0
INFO:root:SUCCESS: Logout succeeded

Something that stood out to me was:

ajavier commented 2 years ago

Two commas in dynamic,,00%.. ? But when I make direct call to the API it accepts also two ",,".

That's definitely an issue. It has to be fixed in the code.

johanreinalda commented 2 years ago

I have the same use case, and the same problem with the 'port' attribute now being available after mac.get() :-)

martbhell commented 2 years ago

I have the same use case, and the same problem with the 'port' attribute now being available after mac.get()

Is that a typo? Now/Not?

martbhell commented 2 years ago

Two commas in dynamic,,00%.. ? But when I make direct call to the API it accepts also two ",,".

That's definitely an issue. It has to be fixed in the code.

Sorry I have not been able to reproduce this bit about double ",,".

I was however able to make the pyaoscx print ports!! See the changes in https://github.com/aruba/pyaoscx/pull/19 (not really proposing them, just trying to show what made it work). The change sure feels ugly :)

Some other interesting things in the debug logs:

dcorderohpe commented 2 years ago

Hi, you can use selector='status' in the mac.get() call to get the port information of the mac, the library was initially intended for configuration, so the default selector is 'writable', as you pointed out earlier, that does result in some gotchas for the modules that can't configure anything, I think the default selector for the module could be made into 'status' instead of 'writable' as a solution to the issue with the port, if you can reproduce the issue with the commas it'd be helpful to see where the issue lies, from the information you've provided it seem to me you're using a 6100, what version of the firmware does it have?

martbhell commented 2 years ago

Hello @dcorderohpe !

Cool!

With selector='status' on the Mac.get() call I can make #19 work without setting the depth or selector to None.

Still need to set the Mac address to lower-case though.

        if "macs" in self.path:
            self.path = self.path.lower()

This CX6100 I'm using has ArubaOS-CX PL.10.09.0010

martbhell commented 2 years ago

Thanks @herodrig !

I tested out latest master branch that had the fix of d5c30e824ee0e805edc9b181bfec8489ca7d1ad2 and I could get the ports nicely :) Thanks!