Example #1
0
/** Tests for the {@link Vpls} class. */
public class VplsTest {

  private static final int NUM_DEVICES = 7;

  private static final MacAddress MAC1 = MacAddress.valueOf("00:00:00:00:00:01");
  private static final MacAddress MAC2 = MacAddress.valueOf("00:00:00:00:00:02");
  private static final MacAddress MAC3 = MacAddress.valueOf("00:00:00:00:00:03");
  private static final MacAddress MAC4 = MacAddress.valueOf("00:00:00:00:00:04");
  private static final MacAddress MAC5 = MacAddress.valueOf("00:00:00:00:00:05");
  private static final MacAddress MAC6 = MacAddress.valueOf("00:00:00:00:00:06");
  private static final MacAddress MAC7 = MacAddress.valueOf("00:00:00:00:00:07");

  private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
  private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");

  private static final PortNumber P1 = PortNumber.portNumber(1);

  private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
  private static final VlanId VLAN2 = VlanId.vlanId((short) 2);

  private static final int PRIORITY_OFFSET = 1000;
  private static final String PREFIX_BROADCAST = "brc";
  private static final String PREFIX_UNICAST = "uni";

  private static final DeviceId DID1 = getDeviceId(1);
  private static final DeviceId DID2 = getDeviceId(2);
  private static final DeviceId DID3 = getDeviceId(3);
  private static final DeviceId DID4 = getDeviceId(4);
  private static final DeviceId DID5 = getDeviceId(5);
  private static final DeviceId DID6 = getDeviceId(6);

  private static final ConnectPoint C1 = new ConnectPoint(DID1, P1);
  private static final ConnectPoint C2 = new ConnectPoint(DID2, P1);
  private static final ConnectPoint C3 = new ConnectPoint(DID3, P1);
  private static final ConnectPoint C4 = new ConnectPoint(DID4, P1);
  private static final ConnectPoint C5 = new ConnectPoint(DID5, P1);
  private static final ConnectPoint C6 = new ConnectPoint(DID6, P1);

  private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
  private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
  private static final HostId HID3 = HostId.hostId(MAC3, VLAN1);
  private static final HostId HID4 = HostId.hostId(MAC4, VLAN2);
  private static final HostId HID5 = HostId.hostId(MAC5, VLAN2);
  private static final HostId HID6 = HostId.hostId(MAC6, VLAN2);
  private static final HostId HID7 = HostId.hostId(MAC7, VlanId.NONE);

  private ApplicationService applicationService;
  private CoreService coreService;
  private HostListener hostListener;
  private Set<Host> hostsAvailable;
  private HostService hostService;
  private IntentService intentService;
  private InterfaceService interfaceService;
  private Vpls vpls;

  private static final String APP_NAME = "org.onosproject.vpls";
  private static final ApplicationId APPID = TestApplicationId.create(APP_NAME);

  private static final ProviderId PID = new ProviderId("of", "foo");

  private static IdGenerator idGenerator;

  @Before
  public void setUp() throws Exception {
    idGenerator = new TestIdGenerator();
    Intent.bindIdGenerator(idGenerator);

    applicationService = createMock(ApplicationService.class);

    coreService = createMock(CoreService.class);
    expect(coreService.registerApplication(APP_NAME)).andReturn(APPID);
    replay(coreService);

    hostsAvailable = Sets.newHashSet();
    hostService = new TestHostService(hostsAvailable);

    intentService = new TestIntentService();

    TestIntentSynchronizer intentSynchronizer = new TestIntentSynchronizer(intentService);

    interfaceService = createMock(InterfaceService.class);
    interfaceService.addListener(anyObject(InterfaceListener.class));
    expectLastCall().anyTimes();
    addIntfConfig();

    vpls = new Vpls();
    vpls.applicationService = applicationService;
    vpls.coreService = coreService;
    vpls.hostService = hostService;
    vpls.intentService = intentService;
    vpls.interfaceService = interfaceService;
    vpls.intentSynchronizer = intentSynchronizer;
  }

  @After
  public void tearDown() {
    Intent.unbindIdGenerator(idGenerator);
  }

  /**
   * Creates the interface configuration. On devices 1, 2 and 3 is configured an interface on port 1
   * with vlan 1. On devices 4, 5 and 6 is configured an interface on port 1 with vlan 2. On device
   * 5 no interfaces are configured.
   */
  private void addIntfConfig() {
    Set<Interface> interfaces = Sets.newHashSet();
    Set<Interface> vlanOneSet = Sets.newHashSet();
    Set<Interface> vlanTwoSet = Sets.newHashSet();

    for (int i = 1; i <= NUM_DEVICES - 1; i++) {
      ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);

      Interface intf = new Interface("intfOne", cp, Collections.emptyList(), null, VlanId.NONE);

      if (i <= 3) {
        intf = new Interface("intfTwo", cp, Collections.emptyList(), null, VLAN1);
        interfaces.add(intf);
        vlanOneSet.add(intf);
      } else if (i > 3 && i <= 6) {
        intf = new Interface("intfThree", cp, Collections.emptyList(), null, VLAN2);
        interfaces.add(intf);
        vlanTwoSet.add(intf);
      }
      expect(interfaceService.getInterfacesByPort(cp)).andReturn(Sets.newHashSet(intf)).anyTimes();
    }
    expect(interfaceService.getInterfacesByVlan(VLAN1)).andReturn(vlanOneSet).anyTimes();
    expect(interfaceService.getInterfacesByVlan(VLAN2)).andReturn(vlanTwoSet).anyTimes();
    expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();

    replay(interfaceService);
  }

  /**
   * Checks the case in which six ports are configured with VLANs but no hosts are registered by the
   * HostService. The first three ports have an interface configured on VLAN1, the other three on
   * VLAN2. The number of intents expected is six: three for VLAN1, three for VLAN2. three sp2mp
   * intents, three mp2sp intents.
   */
  @Test
  public void testActivateNoHosts() {
    vpls.activate();

    List<Intent> expectedIntents = Lists.newArrayList();
    expectedIntents.addAll(generateVlanOneBrc());
    expectedIntents.addAll(generateVlanTwoBrc());

    checkIntents(expectedIntents);
  }

  /**
   * Checks the case in which six ports are configured with VLANs and four hosts are registered by
   * the HostService. The first three ports have an interface configured on VLAN1, the other three
   * on VLAN2. The number of intents expected is twelve: six for VLAN1, six for VLAN2. six sp2mp
   * intents, six mp2sp intents. For VLAN1 IPs are added to demonstrate it doesn't influence the
   * number of intents created.
   */
  @Test
  public void testFourInterfacesConfiguredHostsPresent() {
    Host h1 = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(1), Collections.singleton(IP1));
    Host h2 = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(2), Collections.singleton(IP2));
    Host h3 = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(3), Collections.EMPTY_SET);
    Host h4 = new DefaultHost(PID, HID4, MAC4, VLAN2, getLocation(4), Collections.EMPTY_SET);
    Host h5 = new DefaultHost(PID, HID5, MAC5, VLAN2, getLocation(5), Collections.EMPTY_SET);
    Host h6 = new DefaultHost(PID, HID6, MAC6, VLAN2, getLocation(6), Collections.EMPTY_SET);
    hostsAvailable.addAll(Sets.newHashSet(h1, h2, h3, h4, h5, h6));

    vpls.activate();

    List<Intent> expectedIntents = Lists.newArrayList();
    expectedIntents.addAll(generateVlanOneBrc());
    expectedIntents.addAll(generateVlanOneUni());
    expectedIntents.addAll(generateVlanTwoBrc());
    expectedIntents.addAll(generateVlanTwoUni());

    checkIntents(expectedIntents);
  }

  /**
   * Checks the case in which six ports are configured with VLANs and initially no hosts are
   * registered by the HostService. The first three ports have an interface configured on VLAN1, the
   * other three have an interface configured on VLAN2. When the module starts up, three hosts - on
   * device one, two and three - port 1 (both on VLAN1), are registered by the HostService and
   * events are sent to the application. sp2mp intents are created for all interfaces configured and
   * mp2sp intents are created only for the hosts attached. The number of intents expected is nine:
   * six for VLAN1, three for VLAN2. Six sp2mp intents, three mp2sp intents. IPs are added on the
   * first two hosts only to demonstrate it doesn't influence the number of intents created. An
   * additional host is added on device seven, port one to demonstrate that, even if it's on the
   * same VLAN of other interfaces configured in the system, it doesn't let the application generate
   * intents, since it's not connected to the interface configured.
   */
  @Test
  public void testFourInterfacesThreeHostEventsSameVlan() {
    vpls.activate();

    Host h1 = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(1), Collections.singleton(IP1));
    Host h2 = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(2), Collections.singleton(IP2));
    Host h3 = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(3), Collections.EMPTY_SET);
    Host h7 = new DefaultHost(PID, HID7, MAC7, VLAN1, getLocation(7), Collections.EMPTY_SET);
    hostsAvailable.addAll(Sets.newHashSet(h1, h2, h3, h7));

    hostsAvailable.forEach(
        host -> hostListener.event(new HostEvent(HostEvent.Type.HOST_ADDED, host)));

    List<Intent> expectedIntents = Lists.newArrayList();
    expectedIntents.addAll(generateVlanOneBrc());
    expectedIntents.addAll(generateVlanOneUni());
    expectedIntents.addAll(generateVlanTwoBrc());

    checkIntents(expectedIntents);
  }

  /**
   * Checks the case in which six ports are configured with VLANs and initially no hosts are
   * registered by the HostService. The first three ports have an interface configured on VLAN1, the
   * other three have an interface configured on VLAN2. When the module starts up, two hosts - on
   * device one and four - port 1 (VLAN 1 and VLAN 2), are registered by the HostService and events
   * are sent to the application. sp2mp intents are created for all interfaces configured and no
   * mp2sp intents are created at all, since the minimum number of hosts needed on the same vlan to
   * create mp2sp intents is 2. The number of intents expected is six: three for VLAN1, three for
   * VLAN2. six sp2mp intents, zero mp2sp intents. IPs are added on the first host only to
   * demonstrate it doesn't influence the number of intents created.
   */
  @Test
  public void testFourInterfacesTwoHostEventsDifferentVlan() {
    vpls.activate();

    Host h1 = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(1), Collections.singleton(IP1));
    Host h4 = new DefaultHost(PID, HID4, MAC4, VLAN2, getLocation(4), Collections.EMPTY_SET);
    hostsAvailable.addAll(Sets.newHashSet(h1, h4));

    hostsAvailable.forEach(
        host -> {
          hostListener.event(new HostEvent(HostEvent.Type.HOST_ADDED, host));
        });

    List<Intent> expectedIntents = Lists.newArrayList();
    expectedIntents.addAll(generateVlanOneBrc());
    expectedIntents.addAll(generateVlanTwoBrc());

    checkIntents(expectedIntents);
  }

  /**
   * Checks both that the number of intents in submitted in the intent framework it's equal to the
   * number of intents expected and that all intents are equivalent.
   *
   * @param intents the list of intents expected
   */
  private void checkIntents(List<Intent> intents) {
    assertEquals(intents.size(), intentService.getIntentCount());

    for (Intent intentOne : intents) {
      boolean found = false;
      for (Intent intentTwo : intentService.getIntents()) {
        if (intentOne.key().equals(intentTwo.key())) {
          found = true;
          assertTrue(
              format("Comparing %s and %s", intentOne, intentTwo),
              IntentUtils.intentsAreEqual(intentOne, intentTwo));
          break;
        }
      }
      assertTrue(found);
    }
  }

  /**
   * Generates the list of the expected sp2mp intents for VLAN 1.
   *
   * @return the list of expected sp2mp intents for VLAN 1
   */
  private List<SinglePointToMultiPointIntent> generateVlanOneBrc() {
    Key key = null;

    List<SinglePointToMultiPointIntent> intents = Lists.newArrayList();

    // Building sp2mp intent for H1 - VLAN1
    key = Key.of((PREFIX_BROADCAST + "-" + DID1 + "-" + P1 + "-" + VLAN1), APPID);
    intents.add(buildBrcIntent(key, C1, Sets.newHashSet(C2, C3), VLAN1));

    // Building sp2mp intent for H2 - VLAN1
    key = Key.of((PREFIX_BROADCAST + "-" + DID2 + "-" + P1 + "-" + VLAN1), APPID);
    intents.add(buildBrcIntent(key, C2, Sets.newHashSet(C1, C3), VLAN1));

    // Building sp2mp intent for H3 - VLAN1
    key = Key.of((PREFIX_BROADCAST + "-" + DID3 + "-" + P1 + "-" + VLAN1), APPID);
    intents.add(buildBrcIntent(key, C3, Sets.newHashSet(C1, C2), VLAN1));

    return intents;
  }

  /**
   * Generates the list of the expected mp2sp intents for VLAN 1.
   *
   * @return the list of expected mp2sp intents for VLAN 1
   */
  private List<MultiPointToSinglePointIntent> generateVlanOneUni() {
    Key key = null;

    List<MultiPointToSinglePointIntent> intents = Lists.newArrayList();

    // Building mp2sp intent for H1 - VLAN1
    key = Key.of((PREFIX_UNICAST + "-" + DID1 + "-" + P1 + "-" + VLAN1), APPID);
    intents.add(buildUniIntent(key, Sets.newHashSet(C2, C3), C1, VLAN1, MAC1));

    // Building mp2sp intent for H2 - VLAN1
    key = Key.of((PREFIX_UNICAST + "-" + DID2 + "-" + P1 + "-" + VLAN1), APPID);
    intents.add(buildUniIntent(key, Sets.newHashSet(C1, C3), C2, VLAN1, MAC2));

    // Building mp2sp intent for H3 - VLAN1
    key = Key.of((PREFIX_UNICAST + "-" + DID3 + "-" + P1 + "-" + VLAN1), APPID);
    intents.add(buildUniIntent(key, Sets.newHashSet(C1, C2), C3, VLAN1, MAC3));

    return intents;
  }

  /**
   * Generates the list of the expected sp2mp intents for VLAN 2.
   *
   * @return the list of expected sp2mp intents for VLAN 2
   */
  private List<SinglePointToMultiPointIntent> generateVlanTwoBrc() {
    Key key = null;

    List<SinglePointToMultiPointIntent> intents = Lists.newArrayList();

    // Building sp2mp intent for H4 - VLAN2
    key = Key.of((PREFIX_BROADCAST + "-" + DID4 + "-" + P1 + "-" + VLAN2), APPID);
    intents.add(buildBrcIntent(key, C4, Sets.newHashSet(C5, C6), VLAN2));

    // Building sp2mp intent for H5 - VLAN2
    key = Key.of((PREFIX_BROADCAST + "-" + DID5 + "-" + P1 + "-" + VLAN2), APPID);
    intents.add(buildBrcIntent(key, C5, Sets.newHashSet(C4, C6), VLAN2));

    // Building sp2mp intent for H6 - VLAN2
    key = Key.of((PREFIX_BROADCAST + "-" + DID6 + "-" + P1 + "-" + VLAN2), APPID);
    intents.add(buildBrcIntent(key, C6, Sets.newHashSet(C4, C5), VLAN2));

    return intents;
  }

  /**
   * Generates the list of the expected mp2sp intents for VLAN 2.
   *
   * @return the list of expected mp2sp intents for VLAN 2
   */
  private List<MultiPointToSinglePointIntent> generateVlanTwoUni() {
    Key key = null;

    List<MultiPointToSinglePointIntent> intents = Lists.newArrayList();

    // Building mp2sp intent for H4 - VLAN2
    key = Key.of((PREFIX_UNICAST + "-" + DID4 + "-" + P1 + "-" + VLAN2), APPID);
    intents.add(buildUniIntent(key, Sets.newHashSet(C5, C6), C4, VLAN2, MAC4));

    // Building mp2sp intent for H5 - VLAN2
    key = Key.of((PREFIX_UNICAST + "-" + DID5 + "-" + P1 + "-" + VLAN2), APPID);
    intents.add(buildUniIntent(key, Sets.newHashSet(C4, C6), C5, VLAN2, MAC5));

    // Building mp2sp intent for H6 - VLAN2
    key = Key.of((PREFIX_UNICAST + "-" + DID6 + "-" + P1 + "-" + VLAN2), APPID);
    intents.add(buildUniIntent(key, Sets.newHashSet(C4, C5), C6, VLAN2, MAC6));

    return intents;
  }

  /**
   * Builds a Single Point to Multi Point intent.
   *
   * @param key The intent key
   * @param src The source Connect Point
   * @param dsts The destination Connect Points
   * @return Single Point to Multi Point intent generated.
   */
  private SinglePointToMultiPointIntent buildBrcIntent(
      Key key, ConnectPoint src, Set<ConnectPoint> dsts, VlanId vlanId) {
    SinglePointToMultiPointIntent intent;

    TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();

    TrafficSelector selector =
        DefaultTrafficSelector.builder()
            .matchEthDst(MacAddress.BROADCAST)
            .matchVlanId(vlanId)
            .build();

    intent =
        SinglePointToMultiPointIntent.builder()
            .appId(APPID)
            .key(key)
            .selector(selector)
            .treatment(treatment)
            .ingressPoint(src)
            .egressPoints(dsts)
            .priority(PRIORITY_OFFSET)
            .build();
    return intent;
  }

  /**
   * Builds a Multi Point to Single Point intent.
   *
   * @param key The intent key
   * @param srcs The source Connect Points
   * @param dst The destination Connect Point
   * @return Multi Point to Single Point intent generated.
   */
  private MultiPointToSinglePointIntent buildUniIntent(
      Key key, Set<ConnectPoint> srcs, ConnectPoint dst, VlanId vlanId, MacAddress mac) {
    MultiPointToSinglePointIntent intent;

    TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();

    TrafficSelector.Builder builder =
        DefaultTrafficSelector.builder().matchEthDst(mac).matchVlanId(vlanId);

    TrafficSelector selector = builder.build();

    intent =
        MultiPointToSinglePointIntent.builder()
            .appId(APPID)
            .key(key)
            .selector(selector)
            .treatment(treatment)
            .ingressPoints(srcs)
            .egressPoint(dst)
            .priority(PRIORITY_OFFSET)
            .build();
    return intent;
  }

  /**
   * Returns the device ID of the ith device.
   *
   * @param i device to get the ID of
   * @return the device ID
   */
  private static DeviceId getDeviceId(int i) {
    return DeviceId.deviceId("" + i);
  }

  private static HostLocation getLocation(int i) {
    return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
  }

  /**
   * Represents a fake IntentService class that easily allows to store and retrieve intents without
   * implementing the IntentService logic.
   */
  private class TestIntentService extends IntentServiceAdapter {

    private Set<Intent> intents;

    public TestIntentService() {
      intents = Sets.newHashSet();
    }

    @Override
    public void submit(Intent intent) {
      intents.add(intent);
    }

    @Override
    public long getIntentCount() {
      return intents.size();
    }

    @Override
    public Iterable<Intent> getIntents() {
      return intents;
    }

    @Override
    public Intent getIntent(Key intentKey) {
      for (Intent intent : intents) {
        if (intent.key().equals(intentKey)) {
          return intent;
        }
      }
      return null;
    }
  }

  /**
   * Represents a fake HostService class which allows to add hosts manually in each test, when
   * needed.
   */
  private class TestHostService extends HostServiceAdapter {

    private Set<Host> hosts;

    public TestHostService(Set<Host> hosts) {
      this.hosts = hosts;
    }

    @Override
    public void addListener(HostListener listener) {
      VplsTest.this.hostListener = listener;
    }

    @Override
    public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
      return hosts
          .stream()
          .filter(
              h ->
                  h.location().elementId().equals(connectPoint.elementId())
                      && h.location().port().equals(connectPoint.port()))
          .collect(Collectors.toSet());
    }
  }

  private static class TestIdGenerator implements IdGenerator {

    private final AtomicLong id = new AtomicLong(0);

    @Override
    public long getNewId() {
      return id.getAndIncrement();
    }
  }

  /** Test IntentSynchronizer that passes all intents straight through to the intent service. */
  private class TestIntentSynchronizer
      implements IntentSynchronizationService, IntentSynchronizationAdminService {

    private final IntentService intentService;

    /**
     * Creates a new test intent synchronizer.
     *
     * @param intentService intent service
     */
    public TestIntentSynchronizer(IntentService intentService) {
      this.intentService = intentService;
    }

    @Override
    public void submit(Intent intent) {
      intentService.submit(intent);
    }

    @Override
    public void withdraw(Intent intent) {
      intentService.withdraw(intent);
    }

    @Override
    public void modifyPrimary(boolean isPrimary) {}

    @Override
    public void removeIntents() {}

    @Override
    public void removeIntentsByAppId(ApplicationId applicationId) {}
  }
}
/** Unit tests for PeerConnectivityManager. */
public class PeerConnectivityManagerTest extends AbstractIntentTest {

  private static final ApplicationId APPID = TestApplicationId.create("foo");

  private static final ApplicationId CONFIG_APP_ID = APPID;

  private PeerConnectivityManager peerConnectivityManager;
  private IntentSynchronizationService intentSynchronizer;
  private InterfaceService interfaceService;
  private NetworkConfigService networkConfigService;

  private Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers;
  private Map<String, Interface> interfaces;

  private BgpConfig bgpConfig;

  private List<PointToPointIntent> intentList;

  private final DeviceId deviceId1 = DeviceId.deviceId("of:0000000000000001");
  private final DeviceId deviceId2 = DeviceId.deviceId("of:0000000000000002");
  private final DeviceId deviceId3 = DeviceId.deviceId("of:0000000000000003");

  // Interfaces connected to BGP speakers
  private final ConnectPoint s1Eth100 = new ConnectPoint(deviceId1, PortNumber.portNumber(100));
  private final ConnectPoint s2Eth100 = new ConnectPoint(deviceId2, PortNumber.portNumber(100));
  private final ConnectPoint s3Eth100 = new ConnectPoint(deviceId3, PortNumber.portNumber(100));

  // Interfaces connected to BGP peers
  private final ConnectPoint s1Eth1 = new ConnectPoint(deviceId1, PortNumber.portNumber(1));
  private final ConnectPoint s2Eth1 = new ConnectPoint(deviceId2, PortNumber.portNumber(1));
  private final ConnectPoint s3Eth1 = new ConnectPoint(deviceId3, PortNumber.portNumber(1));

  private static final VlanId NO_VLAN = VlanId.NONE;
  private static final VlanId VLAN10 = VlanId.vlanId(Short.valueOf("10"));
  private static final VlanId VLAN20 = VlanId.vlanId(Short.valueOf("20"));
  private static final VlanId VLAN30 = VlanId.vlanId(Short.valueOf("30"));

  @Before
  public void setUp() throws Exception {
    super.setUp();

    interfaceService = createMock(InterfaceService.class);
    interfaceService.addListener(anyObject(InterfaceListener.class));
    expectLastCall().anyTimes();
    networkConfigService = createMock(NetworkConfigService.class);
    networkConfigService.addListener(anyObject(NetworkConfigListener.class));
    expectLastCall().anyTimes();
    bgpConfig = createMock(BgpConfig.class);

    // These will set expectations on routingConfig and interfaceService
    bgpSpeakers = setUpBgpSpeakers();
    interfaces = Collections.unmodifiableMap(setUpInterfaces());

    initPeerConnectivity();
    intentList = setUpIntentList();
  }

  /**
   * Sets up BGP speakers.
   *
   * @return configured BGP speakers as a map from speaker name to speaker
   */
  private Set<BgpConfig.BgpSpeakerConfig> setUpBgpSpeakers() {

    BgpConfig.BgpSpeakerConfig speaker1 =
        new BgpConfig.BgpSpeakerConfig(
            Optional.empty(),
            NO_VLAN,
            s1Eth100,
            Collections.singleton(IpAddress.valueOf("192.168.10.1")));

    BgpConfig.BgpSpeakerConfig speaker2 =
        new BgpConfig.BgpSpeakerConfig(
            Optional.empty(),
            NO_VLAN,
            s1Eth100,
            Sets.newHashSet(IpAddress.valueOf("192.168.20.1"), IpAddress.valueOf("192.168.30.1")));

    BgpConfig.BgpSpeakerConfig speaker3 =
        new BgpConfig.BgpSpeakerConfig(
            Optional.empty(),
            VLAN30,
            s3Eth100,
            Sets.newHashSet(IpAddress.valueOf("192.168.40.1"), IpAddress.valueOf("192.168.50.1")));

    Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers = Sets.newHashSet();
    bgpSpeakers.add(speaker1);
    bgpSpeakers.add(speaker2);
    bgpSpeakers.add(speaker3);

    return bgpSpeakers;
  }

  /**
   * Sets up logical interfaces, which emulate the configured interfaces in SDN-IP application.
   *
   * @return configured interfaces as a map from interface name to Interface
   */
  private Map<String, Interface> setUpInterfaces() {

    Map<String, Interface> configuredInterfaces = new HashMap<>();

    String interfaceSw1Eth1 = "s1-eth1";
    InterfaceIpAddress ia1 =
        new InterfaceIpAddress(
            IpAddress.valueOf("192.168.10.101"), IpPrefix.valueOf("192.168.10.0/24"));
    Interface intfsw1eth1 =
        new Interface(
            interfaceSw1Eth1,
            s1Eth1,
            Collections.singletonList(ia1),
            MacAddress.valueOf("00:00:00:00:00:01"),
            VlanId.NONE);

    configuredInterfaces.put(interfaceSw1Eth1, intfsw1eth1);

    String interfaceSw2Eth1 = "s2-eth1";
    InterfaceIpAddress ia2 =
        new InterfaceIpAddress(
            IpAddress.valueOf("192.168.20.101"), IpPrefix.valueOf("192.168.20.0/24"));
    Interface intfsw2eth1 =
        new Interface(
            interfaceSw2Eth1,
            s2Eth1,
            Collections.singletonList(ia2),
            MacAddress.valueOf("00:00:00:00:00:02"),
            VlanId.NONE);

    configuredInterfaces.put(interfaceSw2Eth1, intfsw2eth1);

    String interfaceSw2Eth1intf2 = "s2-eth1_2";
    InterfaceIpAddress ia3 =
        new InterfaceIpAddress(
            IpAddress.valueOf("192.168.30.101"), IpPrefix.valueOf("192.168.30.0/24"));
    Interface intfsw2eth1intf2 =
        new Interface(
            interfaceSw2Eth1intf2,
            s2Eth1,
            Collections.singletonList(ia3),
            MacAddress.valueOf("00:00:00:00:00:03"),
            VlanId.NONE);

    configuredInterfaces.put(interfaceSw2Eth1intf2, intfsw2eth1intf2);

    String interfaceSw3Eth1 = "s3-eth1";
    InterfaceIpAddress ia4 =
        new InterfaceIpAddress(
            IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24"));
    Interface intfsw3eth1 =
        new Interface(
            Interface.NO_INTERFACE_NAME,
            s3Eth1,
            ImmutableList.of(ia4),
            MacAddress.valueOf("00:00:00:00:00:04"),
            VLAN10);

    configuredInterfaces.put(interfaceSw3Eth1, intfsw3eth1);

    String interfaceSw3Eth1intf2 = "s3-eth1_2";
    InterfaceIpAddress ia5 =
        new InterfaceIpAddress(
            IpAddress.valueOf("192.168.50.101"), IpPrefix.valueOf("192.168.50.0/24"));
    Interface intfsw3eth1intf2 =
        new Interface(
            Interface.NO_INTERFACE_NAME,
            s3Eth1,
            ImmutableList.of(ia5),
            MacAddress.valueOf("00:00:00:00:00:05"),
            VLAN20);

    configuredInterfaces.put(interfaceSw3Eth1intf2, intfsw3eth1intf2);

    expect(interfaceService.getInterfacesByPort(s1Eth1))
        .andReturn(Collections.singleton(intfsw1eth1))
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.10.101")))
        .andReturn(Collections.singleton(intfsw1eth1))
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.10.1")))
        .andReturn(intfsw1eth1)
        .anyTimes();

    expect(interfaceService.getInterfacesByPort(s2Eth1))
        .andReturn(Collections.singleton(intfsw2eth1))
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.20.101")))
        .andReturn(Collections.singleton(intfsw2eth1))
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.20.1")))
        .andReturn(intfsw2eth1)
        .anyTimes();

    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.30.101")))
        .andReturn(Collections.singleton(intfsw2eth1intf2))
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.30.1")))
        .andReturn(intfsw2eth1intf2)
        .anyTimes();

    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.40.101")))
        .andReturn(Collections.singleton(intfsw3eth1))
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.40.1")))
        .andReturn(intfsw3eth1)
        .anyTimes();

    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.50.101")))
        .andReturn(Collections.singleton(intfsw3eth1intf2))
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.50.1")))
        .andReturn(intfsw3eth1intf2)
        .anyTimes();

    // Non-existent interface used during one of the tests
    expect(
            interfaceService.getInterfacesByPort(
                new ConnectPoint(
                    DeviceId.deviceId("of:0000000000000100"), PortNumber.portNumber(1))))
        .andReturn(null)
        .anyTimes();

    expect(interfaceService.getInterfaces())
        .andReturn(Sets.newHashSet(configuredInterfaces.values()))
        .anyTimes();

    return configuredInterfaces;
  }

  /**
   * Sets up expected point to point intent list.
   *
   * @return point to point intent list
   */
  private List<PointToPointIntent> setUpIntentList() {
    intentList = new ArrayList<>();

    setUpBgpIntents();
    setUpIcmpIntents();

    return intentList;
  }

  /**
   * Constructs a BGP intent and put it into the intentList.
   *
   * <p>The purpose of this method is too simplify the setUpBgpIntents() method, and to make the
   * setUpBgpIntents() easy to read.
   *
   * @param srcVlanId ingress VlanId
   * @param dstVlanId egress VlanId
   * @param srcPrefix source IP prefix to match
   * @param dstPrefix destination IP prefix to match
   * @param srcTcpPort source TCP port to match
   * @param dstTcpPort destination TCP port to match
   * @param srcConnectPoint source connect point for PointToPointIntent
   * @param dstConnectPoint destination connect point for PointToPointIntent
   */
  private void bgpPathintentConstructor(
      VlanId srcVlanId,
      VlanId dstVlanId,
      String srcPrefix,
      String dstPrefix,
      Short srcTcpPort,
      Short dstTcpPort,
      ConnectPoint srcConnectPoint,
      ConnectPoint dstConnectPoint) {

    TrafficSelector.Builder builder =
        DefaultTrafficSelector.builder()
            .matchEthType(Ethernet.TYPE_IPV4)
            .matchIPProtocol(IPv4.PROTOCOL_TCP)
            .matchIPSrc(IpPrefix.valueOf(srcPrefix))
            .matchIPDst(IpPrefix.valueOf(dstPrefix));

    if (!srcVlanId.equals(VlanId.NONE)) {
      builder.matchVlanId(srcVlanId);
    }

    TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();

    if (!dstVlanId.equals(VlanId.NONE)) {
      treatment.setVlanId(dstVlanId);
    }

    if (srcTcpPort != null) {
      builder.matchTcpSrc(TpPort.tpPort(srcTcpPort));
    }
    if (dstTcpPort != null) {
      builder.matchTcpDst(TpPort.tpPort(dstTcpPort));
    }

    Key key =
        Key.of(
            srcPrefix.split("/")[0]
                + "-"
                + dstPrefix.split("/")[0]
                + "-"
                + ((srcTcpPort == null) ? "dst" : "src"),
            APPID);

    PointToPointIntent intent =
        PointToPointIntent.builder()
            .appId(APPID)
            .key(key)
            .selector(builder.build())
            .treatment(treatment.build())
            .ingressPoint(srcConnectPoint)
            .egressPoint(dstConnectPoint)
            .build();

    intentList.add(intent);
  }

  /** Sets up intents for BGP paths. */
  private void setUpBgpIntents() {

    Short bgpPort = 179;

    // Start to build intents between BGP speaker1 and BGP peer1
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.10.101/32", "192.168.10.1/32", null, bgpPort, s1Eth100, s1Eth1);
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.10.101/32", "192.168.10.1/32", bgpPort, null, s1Eth100, s1Eth1);

    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.10.1/32", "192.168.10.101/32", null, bgpPort, s1Eth1, s1Eth100);
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.10.1/32", "192.168.10.101/32", bgpPort, null, s1Eth1, s1Eth100);

    // Start to build intents between BGP speaker1 and BGP peer2
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.20.101/32", "192.168.20.1/32", null, bgpPort, s1Eth100, s2Eth1);
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.20.101/32", "192.168.20.1/32", bgpPort, null, s1Eth100, s2Eth1);

    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.20.1/32", "192.168.20.101/32", null, bgpPort, s2Eth1, s1Eth100);
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.20.1/32", "192.168.20.101/32", bgpPort, null, s2Eth1, s1Eth100);

    // Start to build intents between BGP speaker2 and BGP peer1
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.30.101/32", "192.168.30.1/32", null, bgpPort, s1Eth100, s2Eth1);
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.30.101/32", "192.168.30.1/32", bgpPort, null, s1Eth100, s2Eth1);

    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.30.1/32", "192.168.30.101/32", null, bgpPort, s2Eth1, s1Eth100);
    bgpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.30.1/32", "192.168.30.101/32", bgpPort, null, s2Eth1, s1Eth100);

    // Start to build intents between BGP speaker3 and BGP peer4
    bgpPathintentConstructor(
        VLAN30, VLAN10, "192.168.40.101/32", "192.168.40.1/32", null, bgpPort, s3Eth100, s3Eth1);
    bgpPathintentConstructor(
        VLAN30, VLAN10, "192.168.40.101/32", "192.168.40.1/32", bgpPort, null, s3Eth100, s3Eth1);

    bgpPathintentConstructor(
        VLAN10, VLAN30, "192.168.40.1/32", "192.168.40.101/32", null, bgpPort, s3Eth1, s3Eth100);
    bgpPathintentConstructor(
        VLAN10, VLAN30, "192.168.40.1/32", "192.168.40.101/32", bgpPort, null, s3Eth1, s3Eth100);

    // Start to build intents between BGP speaker3 and BGP peer5
    bgpPathintentConstructor(
        VLAN30, VLAN20, "192.168.50.101/32", "192.168.50.1/32", null, bgpPort, s3Eth100, s3Eth1);
    bgpPathintentConstructor(
        VLAN30, VLAN20, "192.168.50.101/32", "192.168.50.1/32", bgpPort, null, s3Eth100, s3Eth1);

    bgpPathintentConstructor(
        VLAN20, VLAN30, "192.168.50.1/32", "192.168.50.101/32", null, bgpPort, s3Eth1, s3Eth100);
    bgpPathintentConstructor(
        VLAN20, VLAN30, "192.168.50.1/32", "192.168.50.101/32", bgpPort, null, s3Eth1, s3Eth100);
  }

  /**
   * Constructs a BGP intent and put it into the intentList.
   *
   * <p>The purpose of this method is too simplify the setUpBgpIntents() method, and to make the
   * setUpBgpIntents() easy to read.
   *
   * @param srcVlanId ingress VlanId
   * @param dstVlanId egress VlanId
   * @param srcPrefix source IP prefix to match
   * @param dstPrefix destination IP prefix to match
   * @param srcConnectPoint source connect point for PointToPointIntent
   * @param dstConnectPoint destination connect point for PointToPointIntent
   */
  private void icmpPathintentConstructor(
      VlanId srcVlanId,
      VlanId dstVlanId,
      String srcPrefix,
      String dstPrefix,
      ConnectPoint srcConnectPoint,
      ConnectPoint dstConnectPoint) {

    TrafficSelector.Builder builder =
        DefaultTrafficSelector.builder()
            .matchEthType(Ethernet.TYPE_IPV4)
            .matchIPProtocol(IPv4.PROTOCOL_ICMP)
            .matchIPSrc(IpPrefix.valueOf(srcPrefix))
            .matchIPDst(IpPrefix.valueOf(dstPrefix));

    if (!srcVlanId.equals(VlanId.NONE)) {
      builder.matchVlanId(srcVlanId);
    }

    TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();

    if (!dstVlanId.equals(VlanId.NONE)) {
      treatment.setVlanId(dstVlanId);
    }

    Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0] + "-" + "icmp", APPID);

    PointToPointIntent intent =
        PointToPointIntent.builder()
            .appId(APPID)
            .key(key)
            .selector(builder.build())
            .treatment(treatment.build())
            .ingressPoint(srcConnectPoint)
            .egressPoint(dstConnectPoint)
            .build();

    intentList.add(intent);
  }

  /** Sets up intents for ICMP paths. */
  private void setUpIcmpIntents() {
    // Start to build intents between BGP speaker1 and BGP peer1
    icmpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.10.101/32", "192.168.10.1/32", s1Eth100, s1Eth1);
    icmpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.10.1/32", "192.168.10.101/32", s1Eth1, s1Eth100);

    // Start to build intents between BGP speaker1 and BGP peer2
    icmpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.20.101/32", "192.168.20.1/32", s1Eth100, s2Eth1);
    icmpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.20.1/32", "192.168.20.101/32", s2Eth1, s1Eth100);

    icmpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.30.101/32", "192.168.30.1/32", s1Eth100, s2Eth1);
    icmpPathintentConstructor(
        NO_VLAN, NO_VLAN, "192.168.30.1/32", "192.168.30.101/32", s2Eth1, s1Eth100);

    // Start to build intents between BGP speaker3 and BGP peer 4
    icmpPathintentConstructor(
        VLAN10, VLAN30, "192.168.40.1/32", "192.168.40.101/32", s3Eth1, s3Eth100);
    icmpPathintentConstructor(
        VLAN30, VLAN10, "192.168.40.101/32", "192.168.40.1/32", s3Eth100, s3Eth1);

    // Start to build intents between BGP speaker3 and BGP peer 5
    icmpPathintentConstructor(
        VLAN20, VLAN30, "192.168.50.1/32", "192.168.50.101/32", s3Eth1, s3Eth100);
    icmpPathintentConstructor(
        VLAN30, VLAN20, "192.168.50.101/32", "192.168.50.1/32", s3Eth100, s3Eth1);
  }

  /** Initializes peer connectivity testing environment. */
  private void initPeerConnectivity() {
    expect(bgpConfig.bgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
    replay(bgpConfig);
    expect(networkConfigService.getConfig(APPID, BgpConfig.class)).andReturn(bgpConfig).anyTimes();
    replay(networkConfigService);
    replay(interfaceService);

    intentSynchronizer = createMock(IntentSynchronizationService.class);
    replay(intentSynchronizer);

    peerConnectivityManager =
        new PeerConnectivityManager(
            APPID, intentSynchronizer, networkConfigService, CONFIG_APP_ID, interfaceService);
  }

  /**
   * Tests whether peer connectivity manager can set up correct BGP and ICMP intents according to
   * specific configuration.
   *
   * <p>Two tricky cases included in the configuration are: 2 peers on a same switch port, peer on
   * the same switch with BGPd.
   */
  @Test
  public void testConnectionSetup() {
    reset(intentSynchronizer);

    // Setup the expected intents
    for (Intent intent : intentList) {
      intentSynchronizer.submit(eqExceptId(intent));
    }
    replay(intentSynchronizer);

    // Running the interface to be tested.
    peerConnectivityManager.start();

    verify(intentSynchronizer);
  }

  /** Tests a corner case, when there are no interfaces in the configuration. */
  @Test
  public void testNullInterfaces() {
    reset(interfaceService);
    interfaceService.addListener(anyObject(InterfaceListener.class));
    expectLastCall().anyTimes();

    expect(interfaceService.getInterfaces()).andReturn(Sets.newHashSet()).anyTimes();
    expect(interfaceService.getInterfacesByPort(s2Eth1))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getInterfacesByPort(s1Eth1))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.10.101")))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.10.1")))
        .andReturn(null)
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.20.101")))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.20.1")))
        .andReturn(null)
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.30.101")))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.30.1")))
        .andReturn(null)
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.40.101")))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.40.1")))
        .andReturn(null)
        .anyTimes();
    expect(interfaceService.getInterfacesByIp(IpAddress.valueOf("192.168.50.101")))
        .andReturn(Collections.emptySet())
        .anyTimes();
    expect(interfaceService.getMatchingInterface(IpAddress.valueOf("192.168.50.1")))
        .andReturn(null)
        .anyTimes();

    replay(interfaceService);

    reset(intentSynchronizer);
    replay(intentSynchronizer);
    peerConnectivityManager.start();
    verify(intentSynchronizer);
  }

  /** Tests a corner case, when there is no BGP speakers in the configuration. */
  @Test
  public void testNullBgpSpeakers() {
    reset(bgpConfig);
    expect(bgpConfig.bgpSpeakers()).andReturn(Collections.emptySet()).anyTimes();
    replay(bgpConfig);

    // We don't expect any intents in this case
    reset(intentSynchronizer);
    replay(intentSynchronizer);
    peerConnectivityManager.start();
    verify(intentSynchronizer);
  }

  /** Tests a corner case, when there is no Interface configured for one BGP peer. */
  @Test
  public void testNoPeerInterface() {
    IpAddress ip = IpAddress.valueOf("1.1.1.1");
    bgpSpeakers.clear();
    bgpSpeakers.add(
        new BgpConfig.BgpSpeakerConfig(
            Optional.of("foo"), VlanId.NONE, s1Eth100, Collections.singleton(ip)));
    reset(interfaceService);
    interfaceService.addListener(anyObject(InterfaceListener.class));
    expect(interfaceService.getMatchingInterface(ip)).andReturn(null).anyTimes();
    replay(interfaceService);

    // We don't expect any intents in this case
    reset(intentSynchronizer);
    replay(intentSynchronizer);
    peerConnectivityManager.start();
    verify(intentSynchronizer);
  }
}