Skip to content

Commit

Permalink
Mac retrieval for juniper (junos 12 and 14)
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephane Robert committed Aug 17, 2020
1 parent f02e16d commit df3911a
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 2 deletions.
9 changes: 9 additions & 0 deletions netman/adapters/switches/juniper/base.py
Expand Up @@ -799,6 +799,15 @@ def _list_physical_interfaces(self):
def _for_protocol(self, interface_name):
return self.custom_strategies.get_protocols_interface_name(interface_name)

def get_mac_addresses(self):
mac_table = self.netconf.rpc(to_ele("""
<get-ethernet-switching-table-information>
<detail/>
</get-ethernet-switching-table-information>
"""))

return self.custom_strategies.parse_mac_address_table(mac_table)


def all_interfaces():
return new_ele("interfaces")
Expand Down
3 changes: 3 additions & 0 deletions netman/adapters/switches/juniper/mx.py
Expand Up @@ -474,6 +474,9 @@ def get_delete_trunk_vlan_element(self):
def get_delete_vlan_element(self):
return to_ele('<vlan-id operation="delete" />')

def parse_mac_address_table(self, mac_table):
raise NotImplementedError()


def one_interface_vlan(vlan_number, extra_path=""):
def m():
Expand Down
22 changes: 21 additions & 1 deletion netman/adapters/switches/juniper/qfx_copper.py
Expand Up @@ -14,8 +14,9 @@

from ncclient.xml_ import to_ele

from netman.adapters.switches.juniper.base import interface_replace, bond_name, Juniper
from netman.adapters.switches.juniper.base import interface_replace, bond_name, Juniper, first_text
from netman.adapters.switches.juniper.standard import JuniperCustomStrategies
from netman.core.objects.mac_address import MacAddress


def netconf(switch_descriptor):
Expand Down Expand Up @@ -51,3 +52,22 @@ def set_native_vlan_id_node(self, interface_node, native_vlan_id_node):

def get_protocols_interface_name(self, interface_name):
return interface_name

def parse_mac_address_table(self, mac_table):
rejected_interface_names = []
mac = []
for vlan_path in mac_table.xpath("//l2ng-l2ald-mac-entry-vlan"):
vlan = int(first_text(vlan_path.xpath("l2ng-l2-vlan-id")))
for mac_entry in vlan_path.xpath("l2ng-l2-mac-address"):
interface = first_text(vlan_path.xpath("l2ng-l2-mac-logical-interface"))
if interface not in rejected_interface_names:
interface = self._parse_interface_type(interface)
mac_address = first_text(vlan_path.xpath("l2ng-l2-mac-address"))
mac.append(MacAddress(vlan, mac_address, interface))
return mac

def _parse_interface_type(self, interface):
if interface.startswith("ae"):
return "Ag {}".format(interface[2:])
else:
return interface
19 changes: 19 additions & 0 deletions netman/adapters/switches/juniper/standard.py
Expand Up @@ -16,6 +16,7 @@
from netman.adapters.switches.juniper.base import interface_speed, interface_replace, interface_speed_update, \
first_text, bond_name, Juniper, first, value_of, parse_range, to_range
from netman.core.objects.exceptions import BadVlanName, BadVlanNumber, VlanAlreadyExist, UnknownVlan
from netman.core.objects.mac_address import MacAddress


def netconf(switch_descriptor, *args, **kwargs):
Expand Down Expand Up @@ -217,3 +218,21 @@ def get_delete_trunk_vlan_element(self):

def get_delete_vlan_element(self):
return to_ele('<vlan operation="delete" />')

def parse_mac_address_table(self, mac_table):
rejected_interface_names = ['All-members', 'Router']
mac = []
for mac_table in mac_table.xpath("//mac-table-entry"):
interface = first_text(mac_table.xpath("mac-interface"))
if interface not in rejected_interface_names:
interface = self._parse_interface_type(interface)
mac_address = first_text(mac_table.xpath("mac-address"))
vlan = int(first_text(mac_table.xpath("mac-vlan-tag")))
mac.append(MacAddress(vlan, mac_address, interface))
return mac

def _parse_interface_type(self, interface):
if interface.startswith("ae"):
return "Ag {}".format(interface[2:])
else:
return interface
70 changes: 69 additions & 1 deletion tests/adapters/switches/juniper_qfx_copper_test.py
Expand Up @@ -31,7 +31,9 @@
@ignore_deprecation_warnings
def test_factory():
lock = mock.Mock()
switch = juniper.qfx_copper_factory(SwitchDescriptor(hostname='hostname', model='juniper_qfx_copper', username='username', password='password', port=22), lock)
switch = juniper.qfx_copper_factory(
SwitchDescriptor(hostname='hostname', model='juniper_qfx_copper', username='username', password='password',
port=22), lock)

assert_that(switch, instance_of(FlowControlSwitch))
assert_that(switch.wrapped_switch, instance_of(Juniper))
Expand Down Expand Up @@ -651,3 +653,69 @@ def test_change_bond_speed_update_slaves_and_interface_at_same_time(self):
</config>""")).and_return(an_ok_response())

self.switch.set_bond_link_speed(10, '1g')

def test_get_mac_addresses(self):
self.netconf_mock.should_receive("rpc").with_args(is_xml("""
<get-ethernet-switching-table-information>
</get-ethernet-switching-table-information>
""")).and_return(an_rpc_response(textwrap.dedent("""
<l2ng-l2ald-rtb-macdb>
<l2ng-l2ald-mac-entry-vlan style="extensive">
<l2ng-l2-mac-address>00:11:c6:01:53:a7</l2ng-l2-mac-address>
<mac-count-global>8</mac-count-global>
<learnt-mac-count>8</learnt-mac-count>
<l2ng-l2-mac-routing-instance>default-switch</l2ng-l2-mac-routing-instance>
<l2ng-l2-vlan-id>4063</l2ng-l2-vlan-id>
<l2ng-l2-mac-vlan-name>VLAN4063</l2ng-l2-mac-vlan-name>
<l2ng-l2-mac-logical-interface>ae47.0</l2ng-l2-mac-logical-interface>
<l2ng-l2-mac-ifl-generation>240</l2ng-l2-mac-ifl-generation>
<l2ng-l2-mac-entry-flags>in_hash,in_ifd,in_ifl,in_vlan,in_rtt,kernel,in_ifbd</l2ng-l2-mac-entry-flags>
<l2ng-l2-mac-epoch>1</l2ng-l2-mac-epoch>
<l2ng-l2-mac-sequence-number>2</l2ng-l2-mac-sequence-number>
<l2ng-l2-mac-learn-mask>0x00000002</l2ng-l2-mac-learn-mask>
</l2ng-l2ald-mac-entry-vlan>
<l2ng-l2ald-mac-entry-vlan style="extensive">
<l2ng-l2-mac-address>00:11:90:3d:33:58</l2ng-l2-mac-address>
<mac-count-global>6</mac-count-global>
<learnt-mac-count>6</learnt-mac-count>
<l2ng-l2-mac-routing-instance>default-switch</l2ng-l2-mac-routing-instance>
<l2ng-l2-vlan-id>4080</l2ng-l2-vlan-id>
<l2ng-l2-mac-vlan-name>VLAN4080</l2ng-l2-mac-vlan-name>
<l2ng-l2-mac-logical-interface>xe-0/0/6.0</l2ng-l2-mac-logical-interface>
<l2ng-l2-mac-ifl-generation>3045</l2ng-l2-mac-ifl-generation>
<l2ng-l2-mac-entry-flags>in_hash,in_ifd,in_ifl,in_vlan,in_rtt,kernel,in_ifbd</l2ng-l2-mac-entry-flags>
<l2ng-l2-mac-epoch>31</l2ng-l2-mac-epoch>
<l2ng-l2-mac-sequence-number>0</l2ng-l2-mac-sequence-number>
<l2ng-l2-mac-learn-mask>0x00000001</l2ng-l2-mac-learn-mask>
</l2ng-l2ald-mac-entry-vlan>
<l2ng-l2ald-mac-entry-vlan style="extensive">
<l2ng-l2-mac-address>00:11:b7:b4:74:2c</l2ng-l2-mac-address>
<mac-count-global>8</mac-count-global>
<learnt-mac-count>8</learnt-mac-count>
<l2ng-l2-mac-routing-instance>default-switch</l2ng-l2-mac-routing-instance>
<l2ng-l2-vlan-id>4063</l2ng-l2-vlan-id>
<l2ng-l2-mac-vlan-name>VLAN4063</l2ng-l2-mac-vlan-name>
<l2ng-l2-mac-logical-interface>ae47.0</l2ng-l2-mac-logical-interface>
<l2ng-l2-mac-ifl-generation>240</l2ng-l2-mac-ifl-generation>
<l2ng-l2-mac-entry-flags>in_hash,in_ifd,in_ifl,in_vlan,in_rtt,kernel,in_ifbd</l2ng-l2-mac-entry-flags>
<l2ng-l2-mac-epoch>1</l2ng-l2-mac-epoch>
<l2ng-l2-mac-sequence-number>2</l2ng-l2-mac-sequence-number>
<l2ng-l2-mac-learn-mask>0x00000002</l2ng-l2-mac-learn-mask>
</l2ng-l2ald-mac-entry-vlan>
</l2ng-l2ald-rtb-macdb>
""")))
mac_addresses = self.switch.get_mac_addresses()
assert_that(len(mac_addresses), is_(3))

for mac_address in mac_addresses:
if mac_address.mac_address == '00:11:c6:01:53:a7':
assert_that(mac_address.interface, is_('Ag 47.0'))
assert_that(mac_address.vlan, is_(4063))
elif mac_address.mac_address == '00:11:90:3d:33:58':
assert_that(mac_address.interface, is_('xe-0/0/6.0'))
assert_that(mac_address.vlan, is_(4080))
elif mac_address.mac_address == '00:11:b7:b4:74:2c':
assert_that(mac_address.interface, is_('Ag 47.0'))
assert_that(mac_address.vlan, is_(4063))
else:
self.assert_(False, "Invalide mac_address returned : {}".format(mac_address.mac_address))
83 changes: 83 additions & 0 deletions tests/adapters/switches/juniper_test.py
Expand Up @@ -6627,6 +6627,89 @@ def test_rollback_succeeds(self):

self.switch.rollback_transaction()

def test_get_mac_addresses(self):
self.netconf_mock.should_receive("rpc").with_args(is_xml("""
<get-ethernet-switching-table-information>
<detail/>
</get-ethernet-switching-table-information>
""")).and_return(an_rpc_response(textwrap.dedent("""
<ethernet-switching-table-information style="extensive">
<ethernet-switching-table style="extensive">
<mac-table-count>385</mac-table-count>
<mac-table-learned>181</mac-table-learned>
<mac-table-persistent>0</mac-table-persistent>
<mac-table-entry style="extensive">
<mac-vlan>VLAN3991</mac-vlan>
<mac-vlan-tag>3991</mac-vlan-tag>
<mac-address>*</mac-address>
<mac-interface>All-members</mac-interface>
<mac-interfaces-list style="flood-list">
<mac-interfaces>ae2.0</mac-interfaces>
<mac-interfaces>ae1.0</mac-interfaces>
<mac-interfaces>ae0.0</mac-interfaces>
<mac-interfaces>ae19.0</mac-interfaces>
</mac-interfaces-list>
<mac-type>Flood</mac-type>
<mac-action>Forward</mac-action>
<mac-nexthop>1530</mac-nexthop>
</mac-table-entry>
<mac-table-entry style="extensive">
<mac-vlan>VLAN3991</mac-vlan>
<mac-vlan-tag>3991</mac-vlan-tag>
<mac-address>02:e0:52:36:38:01</mac-address>
<mac-interface>ae19.0</mac-interface>
<mac-interfaces-list>
<mac-interfaces>ae19.0</mac-interfaces>
</mac-interfaces-list>
<mac-type>Learn</mac-type>
<mac-age seconds="0">0</mac-age>
<mac-learned-time seconds="23611665">39w0d 6:47:45</mac-learned-time>
<mac-action>Forward</mac-action>
<mac-nexthop>1525</mac-nexthop>
</mac-table-entry>
<mac-table-entry style="extensive">
<mac-vlan>VLAN4063</mac-vlan>
<mac-vlan-tag>4063</mac-vlan-tag>
<mac-address>f8:c0:01:ce:3a:c1</mac-address>
<mac-interface>Router</mac-interface>
<mac-interfaces-list>
<mac-interfaces>Router</mac-interfaces>
</mac-interfaces-list>
<mac-type>Static</mac-type>
<mac-action>Forward</mac-action>
<mac-nexthop>0</mac-nexthop>
</mac-table-entry>
<mac-table-entry style="extensive">
<mac-vlan>VLAN3991</mac-vlan>
<mac-vlan-tag>3991</mac-vlan-tag>
<mac-address>52:d1:aa:9a:b2:2b</mac-address>
<mac-interface>ge-1/0/13.0</mac-interface>
<mac-interfaces-list>
<mac-interfaces>ae0.0</mac-interfaces>
</mac-interfaces-list>
<mac-type>Learn</mac-type>
<mac-age seconds="0">0</mac-age>
<mac-learned-time seconds="52266618">86w2d 22:30:18</mac-learned-time>
<mac-action>Forward</mac-action>
<mac-nexthop>1516</mac-nexthop>
</mac-table-entry>
</ethernet-switching-table>
</ethernet-switching-table-information>
""")))
mac_addresses = self.switch.get_mac_addresses()
assert_that(len(mac_addresses), is_(2))

for mac_address in mac_addresses:
if mac_address.mac_address == '02:e0:52:36:38:01':
assert_that(mac_address.interface, is_('Ag 19.0'))
assert_that(mac_address.vlan, is_(3991))
elif mac_address.mac_address == '52:d1:aa:9a:b2:2b':
assert_that(mac_address.interface, is_('ge-1/0/13.0'))
assert_that(mac_address.vlan, is_(3991))
else:
self.assert_(False, "Invalide mac_address returned : {}".format(mac_address.mac_address))


def a_configuration(inner_data=""):
return an_rpc_response("""
Expand Down

0 comments on commit df3911a

Please sign in to comment.