#!/usr/bin/env python3
"""LLDP admin status

Verify that LLDP admin status is set properly by lldpd

"""
import time
import infamy
import infamy.lldp as lldp

from scapy.all import Ether, sendp
from scapy.contrib.lldp import (
    LLDPDU, LLDPDUChassisID, LLDPDUPortID, LLDPDUTimeToLive, LLDPDUEndOfLLDPDU
)

def capture_traffic(iface, sec):
    with infamy.IsolatedMacVlan(iface) as netns:
        sniffer = infamy.Sniffer(netns, "ether proto 0x88cc")               
        with sniffer:
            print("Capturing network traffic ...")
            time.sleep(sec)
        return sniffer.output()
        
def send_lldp_packet(iface, chassis_id, chassis_id_subtype, ttl=3):
    eth = Ether(src="02:01:02:03:04:05", dst="01:80:C2:00:00:0E", type=0x88cc)
    lldpdu = eth / LLDPDU() 
    lldpdu /= LLDPDUChassisID(subtype=chassis_id_subtype, id=chassis_id)
    lldpdu /= LLDPDUPortID(subtype=5, id=iface) 
    lldpdu /= LLDPDUTimeToLive(ttl=ttl) / LLDPDUEndOfLLDPDU()
    sendp(lldpdu, iface=iface, verbose=False)

def verify_neigh_presence(test, target, port, expect_neighbor):
    """Verify neighbor (host) presence on the target system"""
    neighbors = lldp.get_remote_systems_data(target, port)
    if expect_neighbor and not neighbors:
        print("Expected LLDP neighbor but found none.")
        test.fail()
    if not expect_neighbor and neighbors:
        print("Unexpected LLDP neighbor found.")
        test.fail()    

def verify_admin_status(test, target, port, admin_status, local_capture, remote_detect):
    target.put_config_dicts({
        "ieee802-dot1ab-lldp": {
            "lldp": {
                "port": [{
                    "name": target["data"],
                    "dest-mac-address": "01:80:C2:00:00:0E",
                    "admin-status": admin_status
                }]
            }
        }
    })

    rc = capture_traffic(port, 5)

    if local_capture and "LLDP" not in rc.stdout:
        test.fail()
    if not local_capture and "LLDP" in rc.stdout:
        test.fail()
    
    send_lldp_packet(port, "Chassis ID 007", 7)
    verify_neigh_presence(test, target, target["data"], remote_detect)

with infamy.Test() as test:
    with test.step("Set up topology and attach to target DUT"):
        env = infamy.Env()
        target = env.attach("target", "mgmt")
        _, hdata = env.ltop.xlate("host", "data")
    
    with test.step("Enable target interface and enable LLDP"):
        target.put_config_dicts({
            "ietf-interfaces": {
                "interfaces": {
                    "interface": [{
                        "name": target["data"], 
                        "enabled": True
                    }]
                }
            },
            "ieee802-dot1ab-lldp": {
                "lldp": {
                    "enabled": True, 
                    "message-tx-interval": 1
                }
            }
        })
    
    with test.step("Verify admin-status: 'rx-only'"):
        verify_admin_status(test, target, hdata, "rx-only", False, True)
    with test.step("Verify admin-status: 'tx-only'"):
        verify_admin_status(test, target, hdata, "tx-only", True, False)
    with test.step("Verify admin-status: 'disabled'"):
        verify_admin_status(test, target, hdata, "disabled", False, False)
    with test.step("Verify admin-status: 'tx-and-rx'"): 
        verify_admin_status(test, target, hdata, "tx-and-rx", True, True)
    
    test.succeed()
