Merge "Minor refactor of the EVPN FSM"
This commit is contained in:
commit
59fb77d697
7 files changed
+122
-114
No files matched your search
@@ -74,7 +74,7 @@ class PortBindingLrpEvpnCreateEvent(EVPNPortBindingEvent):
|
||||
vni = int(row.external_ids[svc_const.EVPN_LRP_VNI_EXT_ID_KEY])
|
||||
vid = int(row.external_ids[svc_const.EVPN_LRP_VLAN_EXT_ID_KEY])
|
||||
try:
|
||||
self.fsm.advance(evpn_fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
self.fsm.advance(evpn_fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac=row.mac[0], vni=vni, vid=vid)
|
||||
except evpn_exc.FSMIllegalTransition:
|
||||
LOG.error("Unexpected FSM transition for VRF %s on %s",
|
||||
@@ -88,7 +88,7 @@ class PortBindingLrpEvpnDeleteEvent(EVPNPortBindingEvent):
|
||||
def run(self, event, row, old):
|
||||
vrf = row.options[ovn_const.LR_OPTIONS_DR_VRF_NAME]
|
||||
try:
|
||||
self.fsm.advance(evpn_fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_DELETE,
|
||||
self.fsm.advance(evpn_fsm.EVPNEvent.PORT_BINDING_DELETE,
|
||||
vrf)
|
||||
except evpn_exc.FSMIllegalTransition:
|
||||
LOG.error("Unexpected FSM transition for VRF %s on %s",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import enum
|
||||
import threading
|
||||
|
||||
from oslo_log import log
|
||||
@@ -23,62 +24,68 @@ LOG = log.getLogger(__name__)
|
||||
_FSM_LOCK = threading.Lock()
|
||||
|
||||
|
||||
class Evpn:
|
||||
"""Per EVPN instance tracking FSM state."""
|
||||
|
||||
class EVPNState(enum.Enum):
|
||||
INIT = 'init'
|
||||
WAITING_FOR_ROUTER = 'waiting_for_router'
|
||||
WAITING_FOR_BRIDGE = 'waiting_for_bridge'
|
||||
WAITING_FOR_VRF = 'waiting_for_vrf'
|
||||
WAITING_FOR_PORT_BINDING = 'waiting_for_port_binding'
|
||||
ADVERTISING = 'advertising'
|
||||
DESTROY = 'destroy'
|
||||
|
||||
|
||||
class EVPNEvent(enum.Enum):
|
||||
PORT_BINDING_CREATE = 'port_binding_create'
|
||||
PORT_BINDING_DELETE = 'port_binding_delete'
|
||||
VRF_CREATE = 'vrf_create'
|
||||
VRF_DELETE = 'vrf_delete'
|
||||
|
||||
|
||||
class EVPNInstance:
|
||||
"""Per EVPN instance tracking FSM state."""
|
||||
|
||||
def __init__(self, vrf):
|
||||
self.vrf = vrf
|
||||
self.vrf_up = False
|
||||
self.mac = None
|
||||
self.vni = None
|
||||
self.vid = None
|
||||
self.state = self.INIT
|
||||
self.state = EVPNState.INIT
|
||||
|
||||
|
||||
class EvpnFSM:
|
||||
"""Finite State Machine for EVPN instances.
|
||||
|
||||
Manages one Evpn instance per VRF. Event sources
|
||||
Manages one EVPNInstance per VRF. Event sources
|
||||
(PortBindingLrpEvpnEvent, VrfHandler) call the public
|
||||
methods; the FSM drives state transitions and triggers
|
||||
provisioning actions.
|
||||
"""
|
||||
|
||||
FSM_EVENT_PORT_BINDING_CREATE = 'port_binding_create'
|
||||
FSM_EVENT_PORT_BINDING_DELETE = 'port_binding_delete'
|
||||
FSM_EVENT_VRF_CREATE = 'vrf_create'
|
||||
FSM_EVENT_VRF_DELETE = 'vrf_delete'
|
||||
|
||||
# Transitions in the FSM
|
||||
# Each entry contains the following:
|
||||
# (Current state, Event):(New state, transition callback)
|
||||
# (Current state, Event): (New state, callback(action))
|
||||
TRANSITIONS = {
|
||||
(Evpn.INIT, FSM_EVENT_PORT_BINDING_CREATE):
|
||||
(Evpn.WAITING_FOR_ROUTER, "_set_evpn_bridge"),
|
||||
(Evpn.INIT, FSM_EVENT_VRF_CREATE):
|
||||
(Evpn.WAITING_FOR_BRIDGE, "_set_evpn_router"),
|
||||
(Evpn.WAITING_FOR_ROUTER, FSM_EVENT_VRF_CREATE):
|
||||
(Evpn.ADVERTISING, "_set_evpn_router_and_advertise"),
|
||||
(Evpn.WAITING_FOR_BRIDGE, FSM_EVENT_PORT_BINDING_CREATE):
|
||||
(Evpn.ADVERTISING, "_set_evpn_bridge_and_advertise"),
|
||||
(Evpn.ADVERTISING, FSM_EVENT_PORT_BINDING_DELETE):
|
||||
(Evpn.WAITING_FOR_BRIDGE, "_unset_evpn_bridge_and_unadvertise"),
|
||||
(Evpn.ADVERTISING, FSM_EVENT_VRF_DELETE):
|
||||
(Evpn.WAITING_FOR_ROUTER, "_unset_evpn_router_and_unadvertise"),
|
||||
(Evpn.WAITING_FOR_BRIDGE, FSM_EVENT_VRF_DELETE):
|
||||
(Evpn.DESTROY, "_destroy"),
|
||||
(Evpn.WAITING_FOR_ROUTER, FSM_EVENT_PORT_BINDING_DELETE):
|
||||
(Evpn.DESTROY, "_destroy"),
|
||||
(EVPNState.INIT, EVPNEvent.PORT_BINDING_CREATE):
|
||||
(EVPNState.WAITING_FOR_VRF, "_set_evpn_bridge"),
|
||||
(EVPNState.INIT, EVPNEvent.VRF_CREATE):
|
||||
(EVPNState.WAITING_FOR_PORT_BINDING, "_set_evpn_router"),
|
||||
(EVPNState.WAITING_FOR_VRF, EVPNEvent.VRF_CREATE):
|
||||
(EVPNState.ADVERTISING, "_set_evpn_router_and_advertise"),
|
||||
(EVPNState.WAITING_FOR_PORT_BINDING, EVPNEvent.PORT_BINDING_CREATE):
|
||||
(EVPNState.ADVERTISING, "_set_evpn_bridge_and_advertise"),
|
||||
(EVPNState.ADVERTISING, EVPNEvent.PORT_BINDING_DELETE):
|
||||
(EVPNState.WAITING_FOR_PORT_BINDING,
|
||||
"_unset_evpn_bridge_and_unadvertise"),
|
||||
(EVPNState.ADVERTISING, EVPNEvent.VRF_DELETE):
|
||||
(EVPNState.WAITING_FOR_VRF,
|
||||
"_unset_evpn_router_and_unadvertise"),
|
||||
(EVPNState.WAITING_FOR_VRF, EVPNEvent.PORT_BINDING_DELETE):
|
||||
(EVPNState.DESTROY, "_destroy"),
|
||||
(EVPNState.WAITING_FOR_PORT_BINDING, EVPNEvent.VRF_DELETE):
|
||||
(EVPNState.DESTROY, "_destroy"),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.instances = {} # vrf -> Evpn
|
||||
self.instances = {} # vrf -> EVPNInstance
|
||||
self._svd = None
|
||||
self._cfg = None
|
||||
self._driver = None
|
||||
@@ -93,15 +100,23 @@ class EvpnFSM:
|
||||
evpn.vni = vni
|
||||
evpn.vid = vid
|
||||
|
||||
def _set_evpn_router(self, evpn):
|
||||
evpn.vrf_up = True
|
||||
|
||||
def _set_evpn_router_and_advertise(self, evpn):
|
||||
self._set_evpn_router(evpn)
|
||||
self._advertise(evpn)
|
||||
|
||||
def _set_evpn_bridge_and_advertise(self, evpn, mac, vni, vid):
|
||||
self._set_evpn_bridge(evpn, mac, vni, vid)
|
||||
self._advertise(evpn)
|
||||
|
||||
def _unset_evpn_bridge_and_unadvertise(self, evpn):
|
||||
self._unadvertise(evpn)
|
||||
evpn.mac = None
|
||||
evpn.vni = None
|
||||
evpn.vid = None
|
||||
|
||||
def _set_evpn_router(self, evpn):
|
||||
evpn.vrf_up = True
|
||||
|
||||
def _unset_evpn_router_and_unadvertise(self, evpn):
|
||||
self._unadvertise(evpn)
|
||||
evpn.vrf_up = False
|
||||
@@ -117,36 +132,29 @@ class EvpnFSM:
|
||||
self._driver.delete_router(evpn.vrf, evpn.vni)
|
||||
LOG.debug("EVPN: unadvertised %s", evpn)
|
||||
|
||||
def _set_evpn_router_and_advertise(self, evpn):
|
||||
self._set_evpn_router(evpn)
|
||||
self._advertise(evpn)
|
||||
|
||||
def _set_evpn_bridge_and_advertise(self, evpn, mac, vni, vid):
|
||||
self._set_evpn_bridge(evpn, mac, vni, vid)
|
||||
self._advertise(evpn)
|
||||
|
||||
def _destroy(self, evpn):
|
||||
LOG.debug("EVPN deleted: VRF %s", evpn.vrf)
|
||||
self.instances.pop(evpn.vrf)
|
||||
self.instances.pop(evpn.vrf, None)
|
||||
|
||||
def advance(self, event, vrf, **kwargs):
|
||||
"""Drive FSM state transition for a VRF in response to an event."""
|
||||
with _FSM_LOCK:
|
||||
evpn = self.instances.setdefault(vrf, Evpn(vrf))
|
||||
LOG.debug("Advancing state EVPN: vrf %s", vrf)
|
||||
previous_state = evpn.state
|
||||
evpn = self.instances.setdefault(vrf, EVPNInstance(vrf))
|
||||
try:
|
||||
evpn.state, callback_name = self.TRANSITIONS[
|
||||
new_state, callback_name = self.TRANSITIONS[
|
||||
(evpn.state, event)]
|
||||
except KeyError:
|
||||
raise evpn_exc.FSMIllegalTransition(
|
||||
"Cannot transition from %s to new state!" %
|
||||
(previous_state))
|
||||
"Cannot transition from %s with event %s!" %
|
||||
(evpn.state, event))
|
||||
try:
|
||||
callback = getattr(self, callback_name)
|
||||
except AttributeError:
|
||||
raise evpn_exc.FSMMissingTransitionCallback(
|
||||
"Transition from %s to %s is missing callback function!" %
|
||||
(previous_state, evpn.state))
|
||||
LOG.info("EVPN VRF %s: %s -> %s", vrf, previous_state, evpn.state)
|
||||
"Transition from %s is missing callback function %s!" %
|
||||
(evpn.state, callback_name))
|
||||
previous_state = evpn.state
|
||||
callback(evpn, **kwargs)
|
||||
evpn.state = new_state
|
||||
LOG.info("EVPN %s: %s -> %s (event=%s)",
|
||||
vrf, previous_state, evpn.state, event)
|
||||
@@ -74,7 +74,7 @@ class VrfHandler:
|
||||
for vrf in stale_vrfs:
|
||||
LOG.debug("Stale VRF removed during replay (VRF %s)", vrf)
|
||||
self._evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, vrf)
|
||||
fsm.EVPNEvent.VRF_DELETE, vrf)
|
||||
self._known_vrfs = self._replay_vrfs
|
||||
self._replay_vrfs = None
|
||||
|
||||
@@ -92,7 +92,7 @@ class VrfHandler:
|
||||
if evpnvrf not in self._known_vrfs:
|
||||
self._known_vrfs.add(evpnvrf)
|
||||
LOG.debug("VRF created: %s", evpnvrf)
|
||||
self._evpn_fsm.advance(fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, evpnvrf)
|
||||
self._evpn_fsm.advance(fsm.EVPNEvent.VRF_CREATE, evpnvrf)
|
||||
|
||||
def handle_dellink(self, msg):
|
||||
try:
|
||||
@@ -106,4 +106,4 @@ class VrfHandler:
|
||||
# VRF not previously tracked
|
||||
return
|
||||
LOG.debug("VRF deleted: %s", evpnvrf)
|
||||
self._evpn_fsm.advance(fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, evpnvrf)
|
||||
self._evpn_fsm.advance(fsm.EVPNEvent.VRF_DELETE, evpnvrf)
|
||||
@@ -271,7 +271,7 @@ class PortBindingLrpEvpnCreateEventTestCase(BaseEvpnEventsTestCase):
|
||||
self._wait_for_advance()
|
||||
self.assertIn(vrf, self.fsm.instances)
|
||||
instance = self.fsm.instances[vrf]
|
||||
self.assertEqual(evpn_fsm.Evpn.WAITING_FOR_ROUTER, instance.state)
|
||||
self.assertEqual(evpn_fsm.EVPNState.WAITING_FOR_VRF, instance.state)
|
||||
self.assertEqual(mac, instance.mac)
|
||||
self.assertEqual(vni, instance.vni)
|
||||
self.assertEqual(vlan, instance.vid)
|
||||
|
||||
@@ -165,9 +165,9 @@ class TestFsmSvdIntegration(base.BaseNetlinkTestCase):
|
||||
|
||||
def _advance_to_advertising(self, vni, vid):
|
||||
self._evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, self._vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, self._vrf)
|
||||
self._evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
self._vrf, mac=self.SVI_MAC, vni=vni, vid=vid)
|
||||
|
||||
def test_fsm_advertise_creates_svi(self):
|
||||
@@ -179,7 +179,7 @@ class TestFsmSvdIntegration(base.BaseNetlinkTestCase):
|
||||
self._advance_to_advertising(vni, vid)
|
||||
|
||||
evpn = self._evpn_fsm.instances[self._vrf]
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
self.assertTrue(ip_lib.device_exists(svi_name))
|
||||
|
||||
def test_fsm_port_binding_delete_deletes_svi(self):
|
||||
@@ -192,10 +192,10 @@ class TestFsmSvdIntegration(base.BaseNetlinkTestCase):
|
||||
self.assertTrue(ip_lib.device_exists(svi_name))
|
||||
|
||||
self._evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_DELETE, self._vrf)
|
||||
fsm.EVPNEvent.PORT_BINDING_DELETE, self._vrf)
|
||||
|
||||
evpn = self._evpn_fsm.instances[self._vrf]
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_BRIDGE, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_PORT_BINDING, evpn.state)
|
||||
self.assertFalse(ip_lib.device_exists(svi_name))
|
||||
|
||||
def test_fsm_vrf_delete_deletes_svi(self):
|
||||
@@ -208,10 +208,10 @@ class TestFsmSvdIntegration(base.BaseNetlinkTestCase):
|
||||
self.assertTrue(ip_lib.device_exists(svi_name))
|
||||
|
||||
self._evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, self._vrf)
|
||||
fsm.EVPNEvent.VRF_DELETE, self._vrf)
|
||||
|
||||
evpn = self._evpn_fsm.instances[self._vrf]
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_ROUTER, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_VRF, evpn.state)
|
||||
self.assertFalse(ip_lib.device_exists(svi_name))
|
||||
|
||||
def test_evpn_svd_del_vni_idempotent_without_prior_add(self):
|
||||
|
||||
@@ -37,12 +37,12 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def test_vrf_then_port_binding_create(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='aa:bb:cc:dd:ee:ff', vni=10, vid=1)
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
self.assertEqual('aa:bb:cc:dd:ee:ff', evpn.mac)
|
||||
self.assertEqual(10, evpn.vni)
|
||||
self.assertEqual(1, evpn.vid)
|
||||
@@ -54,12 +54,12 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def test_port_binding_then_vrf_create(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='aa:bb:cc:dd:ee:ff', vni=10, vid=1)
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
self.assertEqual('aa:bb:cc:dd:ee:ff', evpn.mac)
|
||||
self.assertEqual(10, evpn.vni)
|
||||
self.assertEqual(1, evpn.vid)
|
||||
@@ -70,36 +70,36 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
|
||||
def test_advertise_then_port_binding_delete(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.mac = 'aa:bb:cc:dd:ee:ff'
|
||||
evpn.vni = 10
|
||||
evpn.vid = 1
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.ADVERTISING
|
||||
evpn.state = fsm.EVPNState.ADVERTISING
|
||||
self.evpn_fsm.instances[vrf] = evpn
|
||||
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, vrf)
|
||||
fsm.EVPNEvent.VRF_DELETE, vrf)
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_DELETE, vrf)
|
||||
fsm.EVPNEvent.PORT_BINDING_DELETE, vrf)
|
||||
self.assertNotIn(vrf, self.evpn_fsm.instances)
|
||||
self.mock_svd.del_vni.assert_called_once_with(10, 1)
|
||||
self.mock_driver.delete_router.assert_called_once_with(vrf, 10)
|
||||
|
||||
def test_advertise_then_vrf_delete(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.mac = 'aa:bb:cc:dd:ee:ff'
|
||||
evpn.vni = 10
|
||||
evpn.vid = 1
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.ADVERTISING
|
||||
evpn.state = fsm.EVPNState.ADVERTISING
|
||||
self.evpn_fsm.instances[vrf] = evpn
|
||||
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_DELETE, vrf)
|
||||
fsm.EVPNEvent.PORT_BINDING_DELETE, vrf)
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, vrf)
|
||||
fsm.EVPNEvent.VRF_DELETE, vrf)
|
||||
self.assertNotIn(vrf, self.evpn_fsm.instances)
|
||||
self.mock_svd.del_vni.assert_called_once_with(10, 1)
|
||||
self.mock_driver.delete_router.assert_called_once_with(vrf, 10)
|
||||
@@ -112,12 +112,12 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def netlink_thread():
|
||||
barrier.wait()
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
|
||||
def idl_thread():
|
||||
barrier.wait()
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='aa:bb:cc:dd:ee:ff', vni=10, vid=1)
|
||||
|
||||
threads = [threading.Thread(target=netlink_thread),
|
||||
@@ -128,7 +128,7 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
t.join()
|
||||
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
self.assertEqual('aa:bb:cc:dd:ee:ff', evpn.mac)
|
||||
self.assertEqual(10, evpn.vni)
|
||||
self.assertEqual(1, evpn.vid)
|
||||
@@ -137,12 +137,12 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def test_simultaneous_vrf_and_port_binding_delete(self):
|
||||
"""Netlink and SB IDL threads both delete for the same VRF."""
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.mac = 'aa:bb:cc:dd:ee:ff'
|
||||
evpn.vni = 10
|
||||
evpn.vid = 1
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.ADVERTISING
|
||||
evpn.state = fsm.EVPNState.ADVERTISING
|
||||
self.evpn_fsm.instances[vrf] = evpn
|
||||
|
||||
barrier = threading.Barrier(2)
|
||||
@@ -150,12 +150,12 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def netlink_thread():
|
||||
barrier.wait()
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, vrf)
|
||||
fsm.EVPNEvent.VRF_DELETE, vrf)
|
||||
|
||||
def idl_thread():
|
||||
barrier.wait()
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_DELETE, vrf)
|
||||
fsm.EVPNEvent.PORT_BINDING_DELETE, vrf)
|
||||
|
||||
threads = [threading.Thread(target=netlink_thread),
|
||||
threading.Thread(target=idl_thread)]
|
||||
@@ -169,21 +169,21 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def test_advertise_then_vrf_delete_then_vrf_create(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='aa:bb:cc:dd:ee:ff', vni=10, vid=1)
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_DELETE, vrf)
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_ROUTER, evpn.state)
|
||||
fsm.EVPNEvent.VRF_DELETE, vrf)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_VRF, evpn.state)
|
||||
self.mock_svd.del_vni.assert_called_once_with(10, 1)
|
||||
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
self.assertEqual(2, self.mock_svd.add_vni.call_count)
|
||||
self.assertEqual(2, self.mock_driver.create_router.call_count)
|
||||
self.mock_driver.delete_router.assert_called_once_with(vrf, 10)
|
||||
@@ -191,25 +191,25 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def test_advertise_then_port_binding_delete_then_port_binding_create(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='aa:bb:cc:dd:ee:ff', vni=10, vid=1)
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_DELETE, vrf)
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_BRIDGE, evpn.state)
|
||||
fsm.EVPNEvent.PORT_BINDING_DELETE, vrf)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_PORT_BINDING, evpn.state)
|
||||
self.assertIsNone(evpn.mac)
|
||||
self.assertIsNone(evpn.vni)
|
||||
self.assertIsNone(evpn.vid)
|
||||
self.mock_svd.del_vni.assert_called_once_with(10, 1)
|
||||
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='11:22:33:44:55:66', vni=20, vid=2)
|
||||
self.assertEqual(fsm.Evpn.ADVERTISING, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.ADVERTISING, evpn.state)
|
||||
self.assertEqual('11:22:33:44:55:66', evpn.mac)
|
||||
self.assertEqual(20, evpn.vni)
|
||||
self.assertEqual(2, evpn.vid)
|
||||
@@ -220,9 +220,9 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
|
||||
def test_replay_end_deletes_stale_fsm_instance(self):
|
||||
vrf1, vrf2 = 'vr0a1b2c3d-eee', 'vr1a2b3c3d-fff'
|
||||
evpn = fsm.Evpn(vrf1)
|
||||
evpn = fsm.EVPNInstance(vrf1)
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.WAITING_FOR_BRIDGE
|
||||
evpn.state = fsm.EVPNState.WAITING_FOR_PORT_BINDING
|
||||
self.evpn_fsm.instances[vrf1] = evpn
|
||||
handler = netlink_monitor.VrfHandler(self.evpn_fsm)
|
||||
handler._known_vrfs = {vrf1, vrf2}
|
||||
@@ -233,26 +233,26 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
|
||||
def test_replay_end_transitions_advertising_to_waiting(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.mac = 'aa:bb:cc:dd:ee:ff'
|
||||
evpn.vni = 10
|
||||
evpn.vid = 1
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.ADVERTISING
|
||||
evpn.state = fsm.EVPNState.ADVERTISING
|
||||
self.evpn_fsm.instances[vrf] = evpn
|
||||
handler = netlink_monitor.VrfHandler(self.evpn_fsm)
|
||||
handler._known_vrfs = {vrf}
|
||||
handler._replay_vrfs = set()
|
||||
handler.replay_end()
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_ROUTER, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_VRF, evpn.state)
|
||||
self.assertIn(vrf, self.evpn_fsm.instances)
|
||||
self.mock_driver.delete_router.assert_called_once_with(vrf, 10)
|
||||
|
||||
def test_replay_end_no_stale_vrfs(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.WAITING_FOR_BRIDGE
|
||||
evpn.state = fsm.EVPNState.WAITING_FOR_PORT_BINDING
|
||||
self.evpn_fsm.instances[vrf] = evpn
|
||||
handler = netlink_monitor.VrfHandler(self.evpn_fsm)
|
||||
handler._known_vrfs = {vrf}
|
||||
@@ -264,18 +264,18 @@ class TestEvpnFSM(base.BaseTestCase):
|
||||
def test_driver_not_called_before_advertising(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_VRF_CREATE, vrf)
|
||||
fsm.EVPNEvent.VRF_CREATE, vrf)
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_BRIDGE, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_PORT_BINDING, evpn.state)
|
||||
self.mock_driver.create_router.assert_not_called()
|
||||
self.mock_driver.delete_router.assert_not_called()
|
||||
|
||||
def test_driver_not_called_port_binding_before_vrf(self):
|
||||
vrf = 'vr0a1b2c3d-fff'
|
||||
self.evpn_fsm.advance(
|
||||
fsm.EvpnFSM.FSM_EVENT_PORT_BINDING_CREATE,
|
||||
fsm.EVPNEvent.PORT_BINDING_CREATE,
|
||||
vrf, mac='aa:bb:cc:dd:ee:ff', vni=10, vid=1)
|
||||
evpn = self.evpn_fsm.instances[vrf]
|
||||
self.assertEqual(fsm.Evpn.WAITING_FOR_ROUTER, evpn.state)
|
||||
self.assertEqual(fsm.EVPNState.WAITING_FOR_VRF, evpn.state)
|
||||
self.mock_driver.create_router.assert_not_called()
|
||||
self.mock_driver.delete_router.assert_not_called()
|
||||
@@ -154,9 +154,9 @@ class TestVrfHandler(base.BaseTestCase):
|
||||
def test_handle_dellink_evpn_vrf(self):
|
||||
vrf = 'vr0a1b2c3d-ffff'
|
||||
self.handler._known_vrfs.add(vrf)
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.WAITING_FOR_BRIDGE
|
||||
evpn.state = fsm.EVPNState.WAITING_FOR_PORT_BINDING
|
||||
self._evpn_fsm.instances[vrf] = evpn
|
||||
msg = _make_vrf_msg(vrf)
|
||||
self.handler.handle_dellink(msg)
|
||||
@@ -166,9 +166,9 @@ class TestVrfHandler(base.BaseTestCase):
|
||||
def test_handle_dellink_unknown_vrf(self):
|
||||
vrf = 'vr0a1b2c3d-ffff'
|
||||
self.handler._known_vrfs.add(vrf)
|
||||
evpn = fsm.Evpn(vrf)
|
||||
evpn = fsm.EVPNInstance(vrf)
|
||||
evpn.vrf_up = True
|
||||
evpn.state = fsm.Evpn.WAITING_FOR_BRIDGE
|
||||
evpn.state = fsm.EVPNState.WAITING_FOR_PORT_BINDING
|
||||
self._evpn_fsm.instances[vrf] = evpn
|
||||
msg = _make_vrf_msg('vr0a1b2c3d-eeee')
|
||||
self.handler.handle_dellink(msg)
|
||||
|
||||
Reference in new issue
Block a user