import pytest
from pr2test.tools import interface_exists
from pr2test.tools import address_exists
from pr2test.tools import route_exists
from pr2test.context_manager import make_test_matrix


test_matrix = make_test_matrix(targets=['local', 'netns'],
                               dbs=['sqlite3/:memory:', 'postgres/pr2test'])


@pytest.mark.parametrize('context', test_matrix, indirect=True)
def test_create(context):
    ifname = context.new_ifname
    iface = (context
             .ndb
             .interfaces
             .create(ifname=ifname, kind='dummy')
             .commit())
    assert interface_exists(context.netns, ifname=ifname)
    iface.rollback()
    assert not interface_exists(context.netns, ifname=ifname)


@pytest.mark.parametrize('context', test_matrix, indirect=True)
def test_remove(context):
    ifname = context.new_ifname
    iface = (context
             .ndb
             .interfaces
             .create(ifname=ifname, kind='dummy')
             .commit())
    assert interface_exists(context.netns, ifname=ifname)
    iface.remove().commit()
    assert not interface_exists(context.netns, ifname=ifname)
    iface.rollback()
    assert interface_exists(context.netns, ifname=ifname)


@pytest.mark.parametrize('context', test_matrix, indirect=True)
def test_set(context):
    ifname = context.new_ifname
    (context
     .ndb
     .interfaces
     .create(ifname=ifname, kind='dummy', address='00:11:22:33:44:55')
     .commit())
    assert interface_exists(context.netns,
                            ifname=ifname,
                            address='00:11:22:33:44:55')
    (context
     .ndb
     .interfaces[ifname]
     .set('address', '00:11:22:aa:aa:aa')
     .commit())
    assert not interface_exists(context.netns,
                                ifname=ifname,
                                address='00:11:22:33:44:55')
    assert interface_exists(context.netns,
                            ifname=ifname,
                            address='00:11:22:aa:aa:aa')
    (context
     .ndb
     .interfaces[ifname]
     .rollback())
    assert not interface_exists(context.netns,
                                ifname=ifname,
                                address='00:11:22:aa:aa:aa')
    assert interface_exists(context.netns,
                            ifname=ifname,
                            address='00:11:22:33:44:55')


@pytest.mark.parametrize('context', test_matrix, indirect=True)
def test_simple_deps(context):

    ifname = context.new_ifname
    ipaddr = context.new_ipaddr
    router = context.new_ipaddr
    dst = str(context.ipnets[1].network)

    #
    # simple dummy interface with one address and
    # one dependent route
    #
    (context
     .ndb
     .interfaces
     .create(ifname=ifname, kind='dummy')
     .set('state', 'up')
     .add_ip(address=ipaddr, prefixlen=24)
     .commit())

    (context
     .ndb
     .routes
     .create(dst=dst, dst_len=24, gateway=router)
     .commit())

    # check everything is in place
    assert interface_exists(context.netns, ifname=ifname)
    assert address_exists(context.netns, ifname=ifname, address=ipaddr)
    assert route_exists(context.netns, gateway=router, dst=dst, dst_len=24)

    # remove the interface
    iface = context.ndb.interfaces[ifname].remove().commit()

    # check there is no interface, no route
    assert not interface_exists(context.netns, ifname=ifname)
    assert not address_exists(context.netns, ifname=ifname, address=ipaddr)
    assert not route_exists(context.netns, gateway=router, dst=dst, dst_len=24)

    # revert the changes using the implicit last_save
    iface.rollback()

    assert interface_exists(context.netns, ifname=ifname)
    assert address_exists(context.netns, ifname=ifname, address=ipaddr)
    assert route_exists(context.netns, gateway=router, dst=dst, dst_len=24)


@pytest.mark.parametrize('context', test_matrix, indirect=True)
def test_bridge_deps(context):

    if_br0 = context.new_ifname
    if_br0p0 = context.new_ifname
    if_br0p1 = context.new_ifname
    ifaddr1 = context.new_ipaddr
    ifaddr2 = context.new_ipaddr
    router = context.new_ipaddr
    dst = str(context.ipnets[1].network)

    with context.ndb.interfaces as i:
        i.create(ifname=if_br0p0, kind='dummy', state='up').commit()
        i.create(ifname=if_br0p1, kind='dummy', state='up').commit()
        (i.create(ifname=if_br0, kind='bridge', state='up')
         .add_port(if_br0p0)
         .add_port(if_br0p1)
         .add_ip(address=ifaddr1, prefixlen=24)
         .add_ip(address=ifaddr2, prefixlen=24)
         .commit())

    (context
     .ndb
     .routes
     .create(dst=dst, dst_len=24, gateway=router)
     .commit())

    assert interface_exists(context.netns, ifname=if_br0)
    assert interface_exists(context.netns, ifname=if_br0p0)
    assert interface_exists(context.netns, ifname=if_br0p1)
    assert address_exists(context.netns, ifname=if_br0, address=ifaddr1)
    assert address_exists(context.netns, ifname=if_br0, address=ifaddr2)
    assert route_exists(context.netns, gateway=router, dst=dst, dst_len=24)

    # remove the interface
    iface = context.ndb.interfaces[if_br0].remove().commit()

    assert not interface_exists(context.netns, ifname=if_br0)
    assert not address_exists(context.netns, ifname=if_br0, address=ifaddr1)
    assert not address_exists(context.netns, ifname=if_br0, address=ifaddr2)
    assert not route_exists(context.netns, gateway=router, dst=dst, dst_len=24)

    # revert the changes using the implicit last_save
    iface.rollback()
    assert interface_exists(context.netns, ifname=if_br0)
    assert interface_exists(context.netns, ifname=if_br0p0)
    assert interface_exists(context.netns, ifname=if_br0p1)
    assert address_exists(context.netns, ifname=if_br0, address=ifaddr1)
    assert address_exists(context.netns, ifname=if_br0, address=ifaddr2)
    assert route_exists(context.netns, gateway=router, dst=dst, dst_len=24)


@pytest.mark.parametrize('context', test_matrix, indirect=True)
def test_vlan_deps(context):

    if_host = context.new_ifname
    if_vlan = context.new_ifname
    ifaddr1 = context.new_ipaddr
    ifaddr2 = context.new_ipaddr
    router = context.new_ipaddr
    dst = str(context.ipnets[1].network)

    (context
     .ndb
     .interfaces
     .create(ifname=if_host, kind='dummy', state='up')
     .commit())

    (context
     .ndb
     .interfaces
     .create(ifname=if_vlan,
             kind='vlan',
             state='up',
             vlan_id=1001,
             link=if_host)
     .add_ip(address=ifaddr1, prefixlen=24)
     .add_ip(address=ifaddr2, prefixlen=24)
     .commit())

    (context
     .ndb
     .routes
     .create(dst=dst, dst_len=24, gateway=router)
     .commit())

    # check everything is in place
    assert interface_exists(context.netns, ifname=if_host)
    assert interface_exists(context.netns, ifname=if_vlan)
    assert address_exists(context.netns, ifname=if_vlan, address=ifaddr1)
    assert address_exists(context.netns, ifname=if_vlan, address=ifaddr2)
    assert route_exists(context.netns, dst=dst, gateway=router)

    # remove the interface
    iface = context.ndb.interfaces[if_host].remove().commit()

    # check there is no interface, no route
    assert not interface_exists(context.netns, ifname=if_host)
    assert not interface_exists(context.netns, ifname=if_vlan)
    assert not address_exists(context.netns, ifname=if_vlan, address=ifaddr1)
    assert not address_exists(context.netns, ifname=if_vlan, address=ifaddr2)
    assert not route_exists(context.netns, dst=dst, gateway=router)

    # revert the changes using the implicit last_save
    iface.rollback()
    assert interface_exists(context.netns, ifname=if_host)
    assert interface_exists(context.netns, ifname=if_vlan)
    assert address_exists(context.netns, ifname=if_vlan, address=ifaddr1)
    assert address_exists(context.netns, ifname=if_vlan, address=ifaddr2)
    assert route_exists(context.netns, dst=dst, gateway=router)
