/**
  * Verifies that Annotations created by merging {@code annotations} is equal to actual
  * Annotations.
  *
  * @param actual Annotations to check
  * @param annotations
  */
 private static void assertAnnotationsEquals(
     Annotations actual, SparseAnnotations... annotations) {
   SparseAnnotations expected = DefaultAnnotations.builder().build();
   for (SparseAnnotations a : annotations) {
     expected = DefaultAnnotations.union(expected, a);
   }
   assertEquals(expected.keys(), actual.keys());
   for (String key : expected.keys()) {
     assertEquals(expected.value(key), actual.value(key));
   }
 }
Exemple #2
0
  // Produces set of annotations from the given JSON node.
  private SparseAnnotations annotations(JsonNode node) {
    if (node == null) {
      return null;
    }

    DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
    Iterator<String> it = node.fieldNames();
    while (it.hasNext()) {
      String k = it.next();
      builder.set(k, node.get(k).asText());
    }
    return builder.build();
  }
 /**
  * Generates an annotation from an existing annotation and DeviceConfig.
  *
  * @param bdc the device config entity from network config
  * @param an the annotation
  * @return annotation combining both sources
  */
 public static SparseAnnotations combine(BasicDeviceConfig bdc, SparseAnnotations an) {
   DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
   if (bdc.driver() != an.value(AnnotationKeys.DRIVER)) {
     newBuilder.set(AnnotationKeys.DRIVER, bdc.driver());
   }
   if (bdc.name() != null) {
     newBuilder.set(AnnotationKeys.NAME, bdc.name());
   }
   if (bdc.latitude() != DEFAULT_COORD) {
     newBuilder.set(AnnotationKeys.LATITUDE, Double.toString(bdc.latitude()));
   }
   if (bdc.longitude() != DEFAULT_COORD) {
     newBuilder.set(AnnotationKeys.LONGITUDE, Double.toString(bdc.longitude()));
   }
   if (bdc.rackAddress() != null) {
     newBuilder.set(AnnotationKeys.RACK_ADDRESS, bdc.rackAddress());
   }
   if (bdc.owner() != null) {
     newBuilder.set(AnnotationKeys.OWNER, bdc.owner());
   }
   DefaultAnnotations newAnnotations = newBuilder.build();
   return DefaultAnnotations.union(an, newAnnotations);
 }
/** Test of the default link description. */
public class DefaultLinkDescriptionTest {

  private static final DeviceId DID1 = deviceId("of:foo");
  private static final DeviceId DID2 = deviceId("of:bar");
  private static final PortNumber P1 = portNumber(1);
  private static final DefaultAnnotations DA =
      DefaultAnnotations.builder().set("Key", "Value").build();

  @Test
  public void basics() {
    LinkDescription desc = new DefaultLinkDescription(cp(DID1, P1), cp(DID2, P1), DIRECT, DA);
    assertEquals("incorrect src", cp(DID1, P1), desc.src());
    assertEquals("incorrect dst", cp(DID2, P1), desc.dst());
    assertEquals("incorrect type", DIRECT, desc.type());
    assertTrue("incorrect annotatios", desc.toString().contains("Key=Value"));
  }
}
Exemple #5
0
 private void deviceAdded(RestSBDevice nodeId) {
   Preconditions.checkNotNull(nodeId, ISNOTNULL);
   DeviceId deviceId = nodeId.deviceId();
   ChassisId cid = new ChassisId();
   String ipAddress = nodeId.ip().toString();
   SparseAnnotations annotations = DefaultAnnotations.builder().set(IPADDRESS, ipAddress).build();
   DeviceDescription deviceDescription =
       new DefaultDeviceDescription(
           deviceId.uri(),
           Device.Type.SWITCH,
           UNKNOWN,
           UNKNOWN,
           UNKNOWN,
           UNKNOWN,
           cid,
           annotations);
   providerService.deviceConnected(deviceId, deviceDescription);
   nodeId.setActive(true);
   controller.addDevice(nodeId);
 }
Exemple #6
0
  @Override
  public void addServiceVm(CordVtnNode node, ConnectPoint connectPoint) {
    Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
    OpenstackPort vPort = openstackService.port(port);
    if (vPort == null) {
      log.warn("Failed to get OpenstackPort for {}", getPortName(port));
      return;
    }

    MacAddress mac = vPort.macAddress();
    HostId hostId = HostId.hostId(mac);

    Host host = hostService.getHost(hostId);
    if (host != null) {
      // Host is already known to the system, no HOST_ADDED event is triggered in this case.
      // It happens when the application is restarted.
      // TODO check host description if it has all the information
      serviceVmAdded(host);
      return;
    }

    Set<IpAddress> ip = Sets.newHashSet(vPort.fixedIps().values());
    SparseAnnotations annotations =
        DefaultAnnotations.builder()
            .set(OPENSTACK_VM_ID, vPort.deviceId())
            .set(SERVICE_ID, vPort.networkId())
            .set(LOCATION_IP, node.localIp().toString())
            .build();

    HostDescription hostDesc =
        new DefaultHostDescription(
            mac,
            VlanId.NONE,
            new HostLocation(connectPoint, System.currentTimeMillis()),
            ip,
            annotations);

    hostProvider.hostDetected(hostId, hostDesc, false);
  }
 @Override
 public void deviceAdded(NetconfDeviceInfo nodeId) {
   Preconditions.checkNotNull(nodeId, ISNULL);
   DeviceId deviceId = nodeId.getDeviceId();
   // Netconf configuration object
   ChassisId cid = new ChassisId();
   String ipAddress = nodeId.ip().toString();
   SparseAnnotations annotations =
       DefaultAnnotations.builder()
           .set(IPADDRESS, ipAddress)
           .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
           .build();
   DeviceDescription deviceDescription =
       new DefaultDeviceDescription(
           deviceId.uri(),
           Device.Type.SWITCH,
           UNKNOWN,
           UNKNOWN,
           UNKNOWN,
           UNKNOWN,
           cid,
           annotations);
   providerService.deviceConnected(deviceId, deviceDescription);
 }
/** Test of the gossip based distributed DeviceStore implementation. */
public class GossipDeviceStoreTest {

  private static final ProviderId PID = new ProviderId("of", "foo");
  private static final ProviderId PIDA = new ProviderId("of", "bar", true);
  private static final DeviceId DID1 = deviceId("of:foo");
  private static final DeviceId DID2 = deviceId("of:bar");
  private static final String MFR = "whitebox";
  private static final String HW = "1.1.x";
  private static final String SW1 = "3.8.1";
  private static final String SW2 = "3.9.5";
  private static final String SN = "43311-12345";
  private static final ChassisId CID = new ChassisId();

  private static final PortNumber P1 = PortNumber.portNumber(1);
  private static final PortNumber P2 = PortNumber.portNumber(2);
  private static final PortNumber P3 = PortNumber.portNumber(3);

  private static final SparseAnnotations A1 =
      DefaultAnnotations.builder().set("A1", "a1").set("B1", "b1").build();
  private static final SparseAnnotations A1_2 =
      DefaultAnnotations.builder().remove("A1").set("B3", "b3").build();
  private static final SparseAnnotations A2 =
      DefaultAnnotations.builder().set("A2", "a2").set("B2", "b2").build();
  private static final SparseAnnotations A2_2 =
      DefaultAnnotations.builder().remove("A2").set("B4", "b4").build();

  // local node
  private static final NodeId NID1 = new NodeId("local");
  private static final ControllerNode ONOS1 =
      new DefaultControllerNode(NID1, IpAddress.valueOf("127.0.0.1"));

  // remote node
  private static final NodeId NID2 = new NodeId("remote");
  private static final ControllerNode ONOS2 =
      new DefaultControllerNode(NID2, IpAddress.valueOf("127.0.0.2"));
  private static final List<SparseAnnotations> NO_ANNOTATION =
      Collections.<SparseAnnotations>emptyList();

  private TestGossipDeviceStore testGossipDeviceStore;
  private GossipDeviceStore gossipDeviceStore;
  private DeviceStore deviceStore;

  private DeviceClockService deviceClockService = new TestDeviceClockService();
  private ClusterCommunicationService clusterCommunicator;

  @BeforeClass
  public static void setUpBeforeClass() throws Exception {}

  @AfterClass
  public static void tearDownAfterClass() throws Exception {}

  @Before
  public void setUp() throws Exception {
    clusterCommunicator = createNiceMock(ClusterCommunicationService.class);
    clusterCommunicator.addSubscriber(
        anyObject(MessageSubject.class),
        anyObject(ClusterMessageHandler.class),
        anyObject(ExecutorService.class));
    expectLastCall().anyTimes();
    replay(clusterCommunicator);
    ClusterService clusterService = new TestClusterService();

    testGossipDeviceStore =
        new TestGossipDeviceStore(deviceClockService, clusterService, clusterCommunicator);
    testGossipDeviceStore.mastershipService = new TestMastershipService();

    TestDatabaseManager testDatabaseManager = new TestDatabaseManager();
    testDatabaseManager.init(clusterService, clusterCommunicator);
    testGossipDeviceStore.storageService = testDatabaseManager;
    testGossipDeviceStore.deviceClockService = deviceClockService;

    gossipDeviceStore = testGossipDeviceStore;
    gossipDeviceStore.activate();
    deviceStore = gossipDeviceStore;
    verify(clusterCommunicator);
    reset(clusterCommunicator);
  }

  @After
  public void tearDown() throws Exception {
    gossipDeviceStore.deactivate();
  }

  private void putDevice(DeviceId deviceId, String swVersion, SparseAnnotations... annotations) {
    DeviceDescription description =
        new DefaultDeviceDescription(
            deviceId.uri(), SWITCH, MFR, HW, swVersion, SN, CID, annotations);
    reset(clusterCommunicator);
    clusterCommunicator.<InternalDeviceEvent>broadcast(
        anyObject(InternalDeviceEvent.class),
        anyObject(MessageSubject.class),
        anyObject(Function.class));
    expectLastCall().anyTimes();
    replay(clusterCommunicator);
    deviceStore.createOrUpdateDevice(PID, deviceId, description);
    verify(clusterCommunicator);
  }

  private void putDeviceAncillary(
      DeviceId deviceId, String swVersion, SparseAnnotations... annotations) {
    DeviceDescription description =
        new DefaultDeviceDescription(
            deviceId.uri(), SWITCH, MFR, HW, swVersion, SN, CID, annotations);
    deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
  }

  private static void assertDevice(DeviceId id, String swVersion, Device device) {
    assertNotNull(device);
    assertEquals(id, device.id());
    assertEquals(MFR, device.manufacturer());
    assertEquals(HW, device.hwVersion());
    assertEquals(swVersion, device.swVersion());
    assertEquals(SN, device.serialNumber());
  }

  /**
   * Verifies that Annotations created by merging {@code annotations} is equal to actual
   * Annotations.
   *
   * @param actual Annotations to check
   * @param annotations
   */
  private static void assertAnnotationsEquals(
      Annotations actual, SparseAnnotations... annotations) {
    SparseAnnotations expected = DefaultAnnotations.builder().build();
    for (SparseAnnotations a : annotations) {
      expected = DefaultAnnotations.union(expected, a);
    }
    assertEquals(expected.keys(), actual.keys());
    for (String key : expected.keys()) {
      assertEquals(expected.value(key), actual.value(key));
    }
  }

  private static void assertDeviceDescriptionEquals(
      DeviceDescription expected, DeviceDescription actual) {
    if (expected == actual) {
      return;
    }
    assertEquals(expected.deviceURI(), actual.deviceURI());
    assertEquals(expected.hwVersion(), actual.hwVersion());
    assertEquals(expected.manufacturer(), actual.manufacturer());
    assertEquals(expected.serialNumber(), actual.serialNumber());
    assertEquals(expected.swVersion(), actual.swVersion());

    assertAnnotationsEquals(actual.annotations(), expected.annotations());
  }

  private static void assertDeviceDescriptionEquals(
      DeviceDescription expected,
      List<SparseAnnotations> expectedAnnotations,
      DeviceDescription actual) {
    if (expected == actual) {
      return;
    }
    assertEquals(expected.deviceURI(), actual.deviceURI());
    assertEquals(expected.hwVersion(), actual.hwVersion());
    assertEquals(expected.manufacturer(), actual.manufacturer());
    assertEquals(expected.serialNumber(), actual.serialNumber());
    assertEquals(expected.swVersion(), actual.swVersion());

    assertAnnotationsEquals(
        actual.annotations(), expectedAnnotations.toArray(new SparseAnnotations[0]));
  }

  @Test
  public final void testGetDeviceCount() {
    assertEquals("initialy empty", 0, deviceStore.getDeviceCount());

    putDevice(DID1, SW1);
    putDevice(DID2, SW2);
    putDevice(DID1, SW1);

    assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
  }

  @Test
  public final void testGetDevices() {
    assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));

    putDevice(DID1, SW1);
    putDevice(DID2, SW2);
    putDevice(DID1, SW1);

    assertEquals("expect 2 uniq devices", 2, Iterables.size(deviceStore.getDevices()));

    Map<DeviceId, Device> devices = new HashMap<>();
    for (Device device : deviceStore.getDevices()) {
      devices.put(device.id(), device);
    }

    assertDevice(DID1, SW1, devices.get(DID1));
    assertDevice(DID2, SW2, devices.get(DID2));

    // add case for new node?
  }

  @Test
  public final void testGetDevice() {

    putDevice(DID1, SW1);

    assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
    assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
  }

  private void assertInternalDeviceEvent(
      NodeId sender,
      DeviceId deviceId,
      ProviderId providerId,
      DeviceDescription expectedDesc,
      Capture<InternalDeviceEvent> actualEvent,
      Capture<MessageSubject> actualSubject,
      Capture<Function<InternalDeviceEvent, byte[]>> actualEncoder) {
    assertTrue(actualEvent.hasCaptured());
    assertTrue(actualSubject.hasCaptured());
    assertTrue(actualEncoder.hasCaptured());

    assertEquals(GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, actualSubject.getValue());
    assertEquals(deviceId, actualEvent.getValue().deviceId());
    assertEquals(providerId, actualEvent.getValue().providerId());
    assertDeviceDescriptionEquals(expectedDesc, actualEvent.getValue().deviceDescription().value());
  }

  private void assertInternalDeviceEvent(
      NodeId sender,
      DeviceId deviceId,
      ProviderId providerId,
      DeviceDescription expectedDesc,
      List<SparseAnnotations> expectedAnnotations,
      Capture<InternalDeviceEvent> actualEvent,
      Capture<MessageSubject> actualSubject,
      Capture<Function<InternalDeviceEvent, byte[]>> actualEncoder) {
    assertTrue(actualEvent.hasCaptured());
    assertTrue(actualSubject.hasCaptured());
    assertTrue(actualEncoder.hasCaptured());

    assertEquals(GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, actualSubject.getValue());
    assertEquals(deviceId, actualEvent.getValue().deviceId());
    assertEquals(providerId, actualEvent.getValue().providerId());
    assertDeviceDescriptionEquals(
        expectedDesc, expectedAnnotations, actualEvent.getValue().deviceDescription().value());
  }

  @Test
  public final void testCreateOrUpdateDevice() throws IOException {
    DeviceDescription description =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW1, SN, CID);
    Capture<InternalDeviceEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalDeviceEvent, byte[]>> encoder = new Capture<>();

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
    assertEquals(DEVICE_ADDED, event.type());
    assertDevice(DID1, SW1, event.subject());
    verify(clusterCommunicator);
    assertInternalDeviceEvent(NID1, DID1, PID, description, message, subject, encoder);

    DeviceDescription description2 =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW2, SN, CID);
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
    assertEquals(DEVICE_UPDATED, event2.type());
    assertDevice(DID1, SW2, event2.subject());

    verify(clusterCommunicator);
    assertInternalDeviceEvent(NID1, DID1, PID, description2, message, subject, encoder);
    reset(clusterCommunicator);

    assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
  }

  @Test
  public final void testCreateOrUpdateDeviceAncillary() throws IOException {
    // add
    DeviceDescription description =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW1, SN, CID, A2);
    Capture<ClusterMessage> bcast = new Capture<>();

    Capture<InternalDeviceEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalDeviceEvent, byte[]>> encoder = new Capture<>();

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
    assertEquals(DEVICE_ADDED, event.type());
    assertDevice(DID1, SW1, event.subject());
    assertEquals(PIDA, event.subject().providerId());
    assertAnnotationsEquals(event.subject().annotations(), A2);
    assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
    verify(clusterCommunicator);
    assertInternalDeviceEvent(NID1, DID1, PIDA, description, message, subject, encoder);

    // update from primary
    DeviceDescription description2 =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW2, SN, CID, A1);
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);

    DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
    assertEquals(DEVICE_UPDATED, event2.type());
    assertDevice(DID1, SW2, event2.subject());
    assertEquals(PID, event2.subject().providerId());
    assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
    assertTrue(deviceStore.isAvailable(DID1));
    verify(clusterCommunicator);
    assertInternalDeviceEvent(NID1, DID1, PID, description2, message, subject, encoder);

    // no-op update from primary
    resetCommunicatorExpectingNoBroadcast(message, subject, encoder);
    assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));

    verify(clusterCommunicator);
    assertFalse("no broadcast expected", bcast.hasCaptured());

    // For now, Ancillary is ignored once primary appears
    resetCommunicatorExpectingNoBroadcast(message, subject, encoder);

    assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));

    verify(clusterCommunicator);
    assertFalse("no broadcast expected", bcast.hasCaptured());

    // But, Ancillary annotations will be in effect
    DeviceDescription description3 =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW1, SN, CID, A2_2);
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);

    DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
    assertEquals(DEVICE_UPDATED, event3.type());
    // basic information will be the one from Primary
    assertDevice(DID1, SW2, event3.subject());
    assertEquals(PID, event3.subject().providerId());
    // but annotation from Ancillary will be merged
    assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
    assertTrue(deviceStore.isAvailable(DID1));
    verify(clusterCommunicator);
    // note: only annotation from PIDA is sent over the wire
    assertInternalDeviceEvent(
        NID1, DID1, PIDA, description3, asList(union(A2, A2_2)), message, subject, encoder);
  }

  @Test
  public final void testMarkOffline() {

    putDevice(DID1, SW1);
    assertTrue(deviceStore.isAvailable(DID1));

    Capture<InternalDeviceEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalDeviceEvent, byte[]>> encoder = new Capture<>();

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    DeviceEvent event = deviceStore.markOffline(DID1);
    assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
    assertDevice(DID1, SW1, event.subject());
    assertFalse(deviceStore.isAvailable(DID1));
    verify(clusterCommunicator);
    // TODO: verify broadcast message
    assertTrue(message.hasCaptured());

    resetCommunicatorExpectingNoBroadcast(message, subject, encoder);
    DeviceEvent event2 = deviceStore.markOffline(DID1);
    assertNull("No change, no event", event2);
    verify(clusterCommunicator);
    assertFalse(message.hasCaptured());
  }

  @Test
  public final void testUpdatePorts() {
    putDevice(DID1, SW1);
    List<PortDescription> pds =
        Arrays.<PortDescription>asList(
            new DefaultPortDescription(P1, true), new DefaultPortDescription(P2, true));
    Capture<InternalDeviceEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalDeviceEvent, byte[]>> encoder = new Capture<>();

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
    verify(clusterCommunicator);
    // TODO: verify broadcast message
    assertTrue(message.hasCaptured());

    Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
    for (DeviceEvent event : events) {
      assertEquals(PORT_ADDED, event.type());
      assertDevice(DID1, SW1, event.subject());
      assertTrue("PortNumber is one of expected", expectedPorts.remove(event.port().number()));
      assertTrue("Port is enabled", event.port().isEnabled());
    }
    assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());

    List<PortDescription> pds2 =
        Arrays.<PortDescription>asList(
            new DefaultPortDescription(P1, false),
            new DefaultPortDescription(P2, true),
            new DefaultPortDescription(P3, true));

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    events = deviceStore.updatePorts(PID, DID1, pds2);
    verify(clusterCommunicator);
    // TODO: verify broadcast message
    assertTrue(message.hasCaptured());

    assertFalse("event should be triggered", events.isEmpty());
    for (DeviceEvent event : events) {
      PortNumber num = event.port().number();
      if (P1.equals(num)) {
        assertEquals(PORT_UPDATED, event.type());
        assertDevice(DID1, SW1, event.subject());
        assertFalse("Port is disabled", event.port().isEnabled());
      } else if (P2.equals(num)) {
        fail("P2 event not expected.");
      } else if (P3.equals(num)) {
        assertEquals(PORT_ADDED, event.type());
        assertDevice(DID1, SW1, event.subject());
        assertTrue("Port is enabled", event.port().isEnabled());
      } else {
        fail("Unknown port number encountered: " + num);
      }
    }

    List<PortDescription> pds3 =
        Arrays.<PortDescription>asList(
            new DefaultPortDescription(P1, false), new DefaultPortDescription(P2, true));
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    events = deviceStore.updatePorts(PID, DID1, pds3);
    verify(clusterCommunicator);
    // TODO: verify broadcast message
    assertTrue(message.hasCaptured());

    assertFalse("event should be triggered", events.isEmpty());
    for (DeviceEvent event : events) {
      PortNumber num = event.port().number();
      if (P1.equals(num)) {
        fail("P1 event not expected.");
      } else if (P2.equals(num)) {
        fail("P2 event not expected.");
      } else if (P3.equals(num)) {
        assertEquals(PORT_REMOVED, event.type());
        assertDevice(DID1, SW1, event.subject());
        assertTrue("Port was enabled", event.port().isEnabled());
      } else {
        fail("Unknown port number encountered: " + num);
      }
    }
  }

  @Test
  public final void testUpdatePortStatus() {
    putDevice(DID1, SW1);
    List<PortDescription> pds =
        Arrays.<PortDescription>asList(new DefaultPortDescription(P1, true));
    deviceStore.updatePorts(PID, DID1, pds);

    Capture<InternalPortStatusEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalPortStatusEvent, byte[]>> encoder = new Capture<>();

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    final DefaultPortDescription desc = new DefaultPortDescription(P1, false);
    DeviceEvent event = deviceStore.updatePortStatus(PID, DID1, desc);
    assertEquals(PORT_UPDATED, event.type());
    assertDevice(DID1, SW1, event.subject());
    assertEquals(P1, event.port().number());
    assertFalse("Port is disabled", event.port().isEnabled());
    verify(clusterCommunicator);
    assertInternalPortStatusEvent(NID1, DID1, PID, desc, NO_ANNOTATION, message, subject, encoder);
    assertTrue(message.hasCaptured());
  }

  @Test
  public final void testUpdatePortStatusAncillary() throws IOException {
    putDeviceAncillary(DID1, SW1);
    putDevice(DID1, SW1);
    List<PortDescription> pds =
        Arrays.<PortDescription>asList(new DefaultPortDescription(P1, true, A1));
    deviceStore.updatePorts(PID, DID1, pds);

    Capture<InternalPortStatusEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalPortStatusEvent, byte[]>> encoder = new Capture<>();

    // update port from primary
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);

    final DefaultPortDescription desc1 = new DefaultPortDescription(P1, false, A1_2);
    DeviceEvent event = deviceStore.updatePortStatus(PID, DID1, desc1);
    assertEquals(PORT_UPDATED, event.type());
    assertDevice(DID1, SW1, event.subject());
    assertEquals(P1, event.port().number());
    assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
    assertFalse("Port is disabled", event.port().isEnabled());
    verify(clusterCommunicator);
    assertInternalPortStatusEvent(
        NID1, DID1, PID, desc1, asList(A1, A1_2), message, subject, encoder);
    assertTrue(message.hasCaptured());

    // update port from ancillary with no attributes
    resetCommunicatorExpectingNoBroadcast(message, subject, encoder);
    final DefaultPortDescription desc2 = new DefaultPortDescription(P1, true);
    DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1, desc2);
    assertNull("Ancillary is ignored if primary exists", event2);
    verify(clusterCommunicator);
    assertFalse(message.hasCaptured());

    // but, Ancillary annotation update will be notified
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    final DefaultPortDescription desc3 = new DefaultPortDescription(P1, true, A2);
    DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1, desc3);
    assertEquals(PORT_UPDATED, event3.type());
    assertDevice(DID1, SW1, event3.subject());
    assertEquals(P1, event3.port().number());
    assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
    assertFalse("Port is disabled", event3.port().isEnabled());
    verify(clusterCommunicator);
    assertInternalPortStatusEvent(NID1, DID1, PIDA, desc3, asList(A2), message, subject, encoder);
    assertTrue(message.hasCaptured());

    // port only reported from Ancillary will be notified as down
    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
    final DefaultPortDescription desc4 = new DefaultPortDescription(P2, true);
    DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1, desc4);
    assertEquals(PORT_ADDED, event4.type());
    assertDevice(DID1, SW1, event4.subject());
    assertEquals(P2, event4.port().number());
    assertAnnotationsEquals(event4.port().annotations());
    assertFalse("Port is disabled if not given from primary provider", event4.port().isEnabled());
    verify(clusterCommunicator);
    // TODO: verify broadcast message content
    assertInternalPortStatusEvent(
        NID1, DID1, PIDA, desc4, NO_ANNOTATION, message, subject, encoder);
    assertTrue(message.hasCaptured());
  }

  private void assertInternalPortStatusEvent(
      NodeId sender,
      DeviceId did,
      ProviderId pid,
      DefaultPortDescription expectedDesc,
      List<SparseAnnotations> expectedAnnotations,
      Capture<InternalPortStatusEvent> actualEvent,
      Capture<MessageSubject> actualSubject,
      Capture<Function<InternalPortStatusEvent, byte[]>> actualEncoder) {

    assertTrue(actualEvent.hasCaptured());
    assertTrue(actualSubject.hasCaptured());
    assertTrue(actualEncoder.hasCaptured());

    assertEquals(GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, actualSubject.getValue());
    assertEquals(did, actualEvent.getValue().deviceId());
    assertEquals(pid, actualEvent.getValue().providerId());
    assertPortDescriptionEquals(
        expectedDesc, expectedAnnotations, actualEvent.getValue().portDescription().value());
  }

  private void assertPortDescriptionEquals(
      PortDescription expectedDesc,
      List<SparseAnnotations> expectedAnnotations,
      PortDescription actual) {

    assertEquals(expectedDesc.portNumber(), actual.portNumber());
    assertEquals(expectedDesc.isEnabled(), actual.isEnabled());

    assertAnnotationsEquals(
        actual.annotations(), expectedAnnotations.toArray(new SparseAnnotations[0]));
  }

  private <T> void resetCommunicatorExpectingNoBroadcast(
      Capture<T> message, Capture<MessageSubject> subject, Capture<Function<T, byte[]>> encoder) {
    message.reset();
    subject.reset();
    encoder.reset();
    reset(clusterCommunicator);
    replay(clusterCommunicator);
  }

  private <T> void resetCommunicatorExpectingSingleBroadcast(
      Capture<T> message, Capture<MessageSubject> subject, Capture<Function<T, byte[]>> encoder) {

    message.reset();
    subject.reset();
    encoder.reset();
    reset(clusterCommunicator);
    clusterCommunicator.broadcast(capture(message), capture(subject), capture(encoder));
    expectLastCall().once();
    replay(clusterCommunicator);
  }

  @Test
  public final void testGetPorts() {
    putDevice(DID1, SW1);
    putDevice(DID2, SW1);
    List<PortDescription> pds =
        Arrays.<PortDescription>asList(
            new DefaultPortDescription(P1, true), new DefaultPortDescription(P2, true));
    deviceStore.updatePorts(PID, DID1, pds);

    Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
    List<Port> ports = deviceStore.getPorts(DID1);
    for (Port port : ports) {
      assertTrue("Port is enabled", port.isEnabled());
      assertTrue("PortNumber is one of expected", expectedPorts.remove(port.number()));
    }
    assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());

    assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
  }

  @Test
  public final void testGetPort() {
    putDevice(DID1, SW1);
    putDevice(DID2, SW1);
    List<PortDescription> pds =
        Arrays.<PortDescription>asList(
            new DefaultPortDescription(P1, true), new DefaultPortDescription(P2, false));
    deviceStore.updatePorts(PID, DID1, pds);

    Port port1 = deviceStore.getPort(DID1, P1);
    assertEquals(P1, port1.number());
    assertTrue("Port is enabled", port1.isEnabled());

    Port port2 = deviceStore.getPort(DID1, P2);
    assertEquals(P2, port2.number());
    assertFalse("Port is disabled", port2.isEnabled());

    Port port3 = deviceStore.getPort(DID1, P3);
    assertNull("P3 not expected", port3);
  }

  @Test
  public final void testRemoveDevice() {
    putDevice(DID1, SW1, A1);
    List<PortDescription> pds =
        Arrays.<PortDescription>asList(new DefaultPortDescription(P1, true, A2));
    deviceStore.updatePorts(PID, DID1, pds);
    putDevice(DID2, SW1);

    assertEquals(2, deviceStore.getDeviceCount());
    assertEquals(1, deviceStore.getPorts(DID1).size());
    assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
    assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);

    Capture<InternalDeviceEvent> message = new Capture<>();
    Capture<MessageSubject> subject = new Capture<>();
    Capture<Function<InternalDeviceEvent, byte[]>> encoder = new Capture<>();

    resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);

    DeviceEvent event = deviceStore.removeDevice(DID1);
    assertEquals(DEVICE_REMOVED, event.type());
    assertDevice(DID1, SW1, event.subject());

    assertEquals(1, deviceStore.getDeviceCount());
    assertEquals(0, deviceStore.getPorts(DID1).size());
    verify(clusterCommunicator);
    // TODO: verify broadcast message
    assertTrue(message.hasCaptured());

    // putBack Device, Port w/o annotation
    putDevice(DID1, SW1);
    List<PortDescription> pds2 =
        Arrays.<PortDescription>asList(new DefaultPortDescription(P1, true));
    deviceStore.updatePorts(PID, DID1, pds2);

    // annotations should not survive
    assertEquals(2, deviceStore.getDeviceCount());
    assertEquals(1, deviceStore.getPorts(DID1).size());
    assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
    assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
  }

  // If Delegates should be called only on remote events,
  // then Simple* should never call them, thus not test required.
  // TODO add test for Port events when we have them
  @Ignore("Ignore until Delegate spec. is clear.")
  @Test
  public final void testEvents() throws InterruptedException {
    final CountDownLatch addLatch = new CountDownLatch(1);
    DeviceStoreDelegate checkAdd =
        event -> {
          assertEquals(DEVICE_ADDED, event.type());
          assertDevice(DID1, SW1, event.subject());
          addLatch.countDown();
        };
    final CountDownLatch updateLatch = new CountDownLatch(1);
    DeviceStoreDelegate checkUpdate =
        event -> {
          assertEquals(DEVICE_UPDATED, event.type());
          assertDevice(DID1, SW2, event.subject());
          updateLatch.countDown();
        };
    final CountDownLatch removeLatch = new CountDownLatch(1);
    DeviceStoreDelegate checkRemove =
        event -> {
          assertEquals(DEVICE_REMOVED, event.type());
          assertDevice(DID1, SW2, event.subject());
          removeLatch.countDown();
        };

    DeviceDescription description =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW1, SN, CID);
    deviceStore.setDelegate(checkAdd);
    deviceStore.createOrUpdateDevice(PID, DID1, description);
    assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));

    DeviceDescription description2 =
        new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, HW, SW2, SN, CID);
    deviceStore.unsetDelegate(checkAdd);
    deviceStore.setDelegate(checkUpdate);
    deviceStore.createOrUpdateDevice(PID, DID1, description2);
    assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));

    deviceStore.unsetDelegate(checkUpdate);
    deviceStore.setDelegate(checkRemove);
    deviceStore.removeDevice(DID1);
    assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
  }

  private final class TestMastershipService extends MastershipServiceAdapter {
    @Override
    public NodeId getMasterFor(DeviceId deviceId) {
      return NID1;
    }

    @Override
    public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
      return CompletableFuture.completedFuture(null);
    }
  }

  private static final class TestGossipDeviceStore extends GossipDeviceStore {

    public TestGossipDeviceStore(
        DeviceClockService deviceClockService,
        ClusterService clusterService,
        ClusterCommunicationService clusterCommunicator) {
      this.deviceClockService = deviceClockService;
      this.clusterService = clusterService;
      this.clusterCommunicator = clusterCommunicator;
    }
  }

  private static final class TestClusterService extends StaticClusterService {

    public TestClusterService() {
      localNode = ONOS1;
      nodes.put(NID1, ONOS1);
      nodeStates.put(NID1, ACTIVE);

      nodes.put(NID2, ONOS2);
      nodeStates.put(NID2, ACTIVE);
    }
  }

  private final class TestDeviceClockService extends DeviceClockServiceAdapter {

    private final AtomicLong ticker = new AtomicLong();

    @Override
    public Timestamp getTimestamp(DeviceId deviceId) {
      if (DID1.equals(deviceId)) {
        return new MastershipBasedTimestamp(1, ticker.getAndIncrement());
      } else if (DID2.equals(deviceId)) {
        return new MastershipBasedTimestamp(2, ticker.getAndIncrement());
      } else {
        throw new IllegalStateException();
      }
    }

    @Override
    public boolean isTimestampAvailable(DeviceId deviceId) {
      return DID1.equals(deviceId) || DID2.equals(deviceId);
    }
  }

  private class TestDatabaseManager extends DatabaseManager {
    void init(ClusterService clusterService, ClusterCommunicationService clusterCommunicator) {
      this.clusterService = clusterService;
      this.clusterCommunicator = clusterCommunicator;
    }
  }
}
/** Test codifying the group service & group provider service contracts. */
public class GroupManagerTest {

  private static final ProviderId PID = new ProviderId("of", "groupfoo");
  private static final DeviceId DID = DeviceId.deviceId("of:001");
  private static final ProviderId FOO_PID = new ProviderId("foo", "foo");
  private static final DeviceId FOO_DID = DeviceId.deviceId("foo:002");

  private static final DefaultAnnotations ANNOTATIONS =
      DefaultAnnotations.builder().set(AnnotationKeys.DRIVER, "foo").build();

  private static final Device FOO_DEV =
      new DefaultDevice(FOO_PID, FOO_DID, Device.Type.SWITCH, "", "", "", "", null, ANNOTATIONS);

  private GroupManager mgr;
  private GroupService groupService;
  private GroupProviderRegistry providerRegistry;
  private TestGroupListener internalListener = new TestGroupListener();
  private GroupListener listener = internalListener;
  private TestGroupProvider internalProvider;
  private GroupProvider provider;
  private GroupProviderService providerService;
  private ApplicationId appId;
  private TestDriverManager driverService;

  @Before
  public void setUp() {
    mgr = new GroupManager();
    groupService = mgr;
    // mgr.deviceService = new DeviceManager();
    mgr.deviceService = new TestDeviceService();
    mgr.cfgService = new ComponentConfigAdapter();
    mgr.store = new SimpleGroupStore();
    injectEventDispatcher(mgr, new TestEventDispatcher());
    providerRegistry = mgr;

    mgr.activate(null);
    mgr.addListener(listener);

    driverService = new TestDriverManager();
    driverService.addDriver(
        new DefaultDriver(
            "foo",
            ImmutableList.of(),
            "",
            "",
            "",
            ImmutableMap.of(GroupProgrammable.class, TestGroupProgrammable.class),
            ImmutableMap.of()));

    internalProvider = new TestGroupProvider(PID);
    provider = internalProvider;
    providerService = providerRegistry.register(provider);
    appId = new DefaultApplicationId(2, "org.groupmanager.test");
    assertTrue(
        "provider should be registered", providerRegistry.getProviders().contains(provider.id()));
  }

  @After
  public void tearDown() {
    providerRegistry.unregister(provider);
    assertFalse(
        "provider should not be registered",
        providerRegistry.getProviders().contains(provider.id()));
    mgr.removeListener(listener);
    mgr.deactivate();
    injectEventDispatcher(mgr, null);
  }

  /** Tests group creation before the device group AUDIT completes. */
  @Test
  public void testGroupServiceBasics() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(DID);
  }

  /** Tests initial device group AUDIT process. */
  @Test
  public void testGroupServiceInitialAudit() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(DID);
    // Test initial group audit process
    testInitialAuditWithPendingGroupRequests(DID);
  }

  /** Tests deletion process of any extraneous groups. */
  @Test
  public void testGroupServiceAuditExtraneous() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(DID);

    // Test audit with extraneous and missing groups
    testAuditWithExtraneousMissingGroups(DID);
  }

  /**
   * Tests re-apply process of any missing groups tests execution of any pending group creation
   * request after the device group AUDIT completes and tests event notifications after receiving
   * confirmation for any operations from data plane.
   */
  @Test
  public void testGroupServiceAuditConfirmed() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(DID);

    // Test audit with extraneous and missing groups
    testAuditWithExtraneousMissingGroups(DID);

    // Test audit with confirmed groups
    testAuditWithConfirmedGroups(DID);
  }

  /** Tests group bucket modifications (additions and deletions) and Tests group deletion. */
  @Test
  public void testGroupServiceBuckets() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(DID);
    programmableTestCleanUp();

    testAuditWithExtraneousMissingGroups(DID);
    // Test group add bucket operations
    testAddBuckets(DID);

    // Test group remove bucket operations
    testRemoveBuckets(DID);

    // Test group remove operations
    testRemoveGroup(DID);
  }

  /** Tests group creation before the device group AUDIT completes with fallback provider. */
  @Test
  public void testGroupServiceFallbackBasics() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(FOO_DID);
    programmableTestCleanUp();
  }

  /** Tests initial device group AUDIT process with fallback provider. */
  @Test
  public void testGroupServiceFallbackInitialAudit() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(FOO_DID);
    programmableTestCleanUp();
    // Test initial group audit process
    testInitialAuditWithPendingGroupRequests(FOO_DID);
  }

  /** Tests deletion process of any extraneous groups with fallback provider. */
  @Test
  public void testGroupServiceFallbackAuditExtraneous() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(FOO_DID);
    programmableTestCleanUp();

    // Test audit with extraneous and missing groups
    testAuditWithExtraneousMissingGroups(FOO_DID);
  }

  /**
   * Tests re-apply process of any missing groups tests execution of any pending group creation
   * request after the device group AUDIT completes and tests event notifications after receiving
   * confirmation for any operations from data plane with fallback provider.
   */
  @Test
  public void testGroupServiceFallbackAuditConfirmed() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(FOO_DID);
    programmableTestCleanUp();

    // Test audit with extraneous and missing groups
    testAuditWithExtraneousMissingGroups(FOO_DID);

    // Test audit with confirmed groups
    testAuditWithConfirmedGroups(FOO_DID);
  }

  /**
   * Tests group bucket modifications (additions and deletions) and Tests group deletion with
   * fallback provider.
   */
  @Test
  public void testGroupServiceFallbackBuckets() {
    // Test Group creation before AUDIT process
    testGroupCreationBeforeAudit(FOO_DID);
    programmableTestCleanUp();

    testAuditWithExtraneousMissingGroups(FOO_DID);
    // Test group add bucket operations
    testAddBuckets(FOO_DID);

    // Test group remove bucket operations
    testRemoveBuckets(FOO_DID);

    // Test group remove operations
    testRemoveGroup(FOO_DID);
  }

  private void programmableTestCleanUp() {
    groupOperations.clear();
    lastDeviceIdProgrammable = null;
  }

  // Test Group creation before AUDIT process
  private void testGroupCreationBeforeAudit(DeviceId deviceId) {
    PortNumber[] ports1 = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
    PortNumber[] ports2 = {PortNumber.portNumber(41), PortNumber.portNumber(42)};
    GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
    List<GroupBucket> buckets = new ArrayList<>();
    List<PortNumber> outPorts = new ArrayList<>();
    outPorts.addAll(Arrays.asList(ports1));
    outPorts.addAll(Arrays.asList(ports2));
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupBuckets = new GroupBuckets(buckets);
    GroupDescription newGroupDesc =
        new DefaultGroupDescription(deviceId, Group.Type.SELECT, groupBuckets, key, null, appId);
    groupService.addGroup(newGroupDesc);
    assertEquals(null, groupService.getGroup(deviceId, key));
    assertEquals(0, Iterables.size(groupService.getGroups(deviceId, appId)));
  }

  // Test initial AUDIT process with pending group requests
  private void testInitialAuditWithPendingGroupRequests(DeviceId deviceId) {
    PortNumber[] ports1 = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
    PortNumber[] ports2 = {PortNumber.portNumber(41), PortNumber.portNumber(42)};
    GroupId gId1 = new DefaultGroupId(1);
    Group group1 = createSouthboundGroupEntry(gId1, Arrays.asList(ports1), 0, deviceId);
    GroupId gId2 = new DefaultGroupId(2);
    // Non zero reference count will make the group manager to queue
    // the extraneous groups until reference count is zero.
    Group group2 = createSouthboundGroupEntry(gId2, Arrays.asList(ports2), 2, deviceId);
    List<Group> groupEntries = Arrays.asList(group1, group2);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    // First group metrics would trigger the device audit completion
    // post which all pending group requests are also executed.
    GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
    Group createdGroup = groupService.getGroup(deviceId, key);
    int createdGroupId = createdGroup.id().id();
    assertNotEquals(gId1.id(), createdGroupId);
    assertNotEquals(gId2.id(), createdGroupId);

    List<GroupOperation> expectedGroupOps =
        Arrays.asList(
            GroupOperation.createDeleteGroupOperation(gId1, Group.Type.SELECT),
            GroupOperation.createAddGroupOperation(
                createdGroup.id(), Group.Type.SELECT, createdGroup.buckets()));
    if (deviceId.equals(DID)) {
      internalProvider.validate(deviceId, expectedGroupOps);
    } else {
      this.validate(deviceId, expectedGroupOps);
    }
  }

  // Test AUDIT process with extraneous groups and missing groups
  private void testAuditWithExtraneousMissingGroups(DeviceId deviceId) {
    PortNumber[] ports1 = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
    PortNumber[] ports2 = {PortNumber.portNumber(41), PortNumber.portNumber(42)};
    GroupId gId1 = new DefaultGroupId(1);
    Group group1 = createSouthboundGroupEntry(gId1, Arrays.asList(ports1), 0, deviceId);
    GroupId gId2 = new DefaultGroupId(2);
    Group group2 = createSouthboundGroupEntry(gId2, Arrays.asList(ports2), 0, deviceId);
    List<Group> groupEntries = Arrays.asList(group1, group2);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
    Group createdGroup = groupService.getGroup(deviceId, key);
    List<GroupOperation> expectedGroupOps =
        Arrays.asList(
            GroupOperation.createDeleteGroupOperation(gId1, Group.Type.SELECT),
            GroupOperation.createDeleteGroupOperation(gId2, Group.Type.SELECT),
            GroupOperation.createAddGroupOperation(
                createdGroup.id(), Group.Type.SELECT, createdGroup.buckets()));
    if (deviceId.equals(DID)) {
      internalProvider.validate(deviceId, expectedGroupOps);
    } else {
      this.validate(deviceId, expectedGroupOps);
    }
  }

  // Test AUDIT with confirmed groups
  private void testAuditWithConfirmedGroups(DeviceId deviceId) {
    GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
    Group createdGroup = groupService.getGroup(deviceId, key);
    createdGroup =
        new DefaultGroup(createdGroup.id(), deviceId, Group.Type.SELECT, createdGroup.buckets());
    List<Group> groupEntries = Collections.singletonList(createdGroup);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_ADDED));
  }

  // Test group add bucket operations
  private void testAddBuckets(DeviceId deviceId) {
    GroupKey addKey = new DefaultGroupKey("group1AddBuckets".getBytes());

    GroupKey prevKey = new DefaultGroupKey("group1BeforeAudit".getBytes());
    Group createdGroup = groupService.getGroup(deviceId, prevKey);
    List<GroupBucket> buckets = new ArrayList<>();
    buckets.addAll(createdGroup.buckets().buckets());

    PortNumber[] addPorts = {PortNumber.portNumber(51), PortNumber.portNumber(52)};
    List<PortNumber> outPorts;
    outPorts = new ArrayList<>();
    outPorts.addAll(Arrays.asList(addPorts));
    List<GroupBucket> addBuckets;
    addBuckets = new ArrayList<>();
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      addBuckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
    groupService.addBucketsToGroup(deviceId, prevKey, groupAddBuckets, addKey, appId);
    GroupBuckets updatedBuckets = new GroupBuckets(buckets);
    List<GroupOperation> expectedGroupOps =
        Collections.singletonList(
            GroupOperation.createModifyGroupOperation(
                createdGroup.id(), Group.Type.SELECT, updatedBuckets));
    if (deviceId.equals(DID)) {
      internalProvider.validate(deviceId, expectedGroupOps);
    } else {
      this.validate(deviceId, expectedGroupOps);
    }
    Group existingGroup = groupService.getGroup(deviceId, addKey);
    List<Group> groupEntries = Collections.singletonList(existingGroup);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED));
  }

  // Test group remove bucket operations
  private void testRemoveBuckets(DeviceId deviceId) {
    GroupKey removeKey = new DefaultGroupKey("group1RemoveBuckets".getBytes());

    GroupKey prevKey = new DefaultGroupKey("group1AddBuckets".getBytes());
    Group createdGroup = groupService.getGroup(deviceId, prevKey);
    List<GroupBucket> buckets = new ArrayList<>();
    buckets.addAll(createdGroup.buckets().buckets());

    PortNumber[] removePorts = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
    List<PortNumber> outPorts = new ArrayList<>();
    outPorts.addAll(Arrays.asList(removePorts));
    List<GroupBucket> removeBuckets = new ArrayList<>();
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      removeBuckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
      buckets.remove(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
    groupService.removeBucketsFromGroup(deviceId, prevKey, groupRemoveBuckets, removeKey, appId);
    GroupBuckets updatedBuckets = new GroupBuckets(buckets);
    List<GroupOperation> expectedGroupOps =
        Collections.singletonList(
            GroupOperation.createModifyGroupOperation(
                createdGroup.id(), Group.Type.SELECT, updatedBuckets));
    if (deviceId.equals(DID)) {
      internalProvider.validate(deviceId, expectedGroupOps);
    } else {
      this.validate(deviceId, expectedGroupOps);
    }
    Group existingGroup = groupService.getGroup(deviceId, removeKey);
    List<Group> groupEntries = Collections.singletonList(existingGroup);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED));
  }

  // Test group remove operations
  private void testRemoveGroup(DeviceId deviceId) {
    GroupKey currKey = new DefaultGroupKey("group1RemoveBuckets".getBytes());
    Group existingGroup = groupService.getGroup(deviceId, currKey);
    groupService.removeGroup(deviceId, currKey, appId);
    List<GroupOperation> expectedGroupOps =
        Collections.singletonList(
            GroupOperation.createDeleteGroupOperation(existingGroup.id(), Group.Type.SELECT));
    if (deviceId.equals(DID)) {
      internalProvider.validate(deviceId, expectedGroupOps);
    } else {
      this.validate(deviceId, expectedGroupOps);
    }
    List<Group> groupEntries = Collections.emptyList();
    providerService.pushGroupMetrics(deviceId, groupEntries);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_REMOVED));
  }

  /**
   * Test GroupOperationFailure function in Group Manager. a)GroupAddFailure b)GroupUpdateFailure
   * c)GroupRemoteFailure
   */
  @Test
  public void testGroupOperationFailure() {
    groupOperationFaliure(DID);
  }

  /**
   * Test GroupOperationFailure function in Group Manager with fallback provider. a)GroupAddFailure
   * b)GroupUpdateFailure c)GroupRemoteFailure
   */
  @Test
  public void testGroupOperationFailureFallBack() {
    groupOperationFaliure(FOO_DID);
  }

  private void groupOperationFaliure(DeviceId deviceId) {
    PortNumber[] ports1 = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
    PortNumber[] ports2 = {PortNumber.portNumber(41), PortNumber.portNumber(42)};
    // Test Group creation before AUDIT process
    GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
    List<GroupBucket> buckets = new ArrayList<>();
    List<PortNumber> outPorts = new ArrayList<>();
    outPorts.addAll(Arrays.asList(ports1));
    outPorts.addAll(Arrays.asList(ports2));
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupBuckets = new GroupBuckets(buckets);
    GroupDescription newGroupDesc =
        new DefaultGroupDescription(deviceId, Group.Type.SELECT, groupBuckets, key, null, appId);
    groupService.addGroup(newGroupDesc);

    // Test initial group audit process
    GroupId gId1 = new DefaultGroupId(1);
    Group group1 = createSouthboundGroupEntry(gId1, Arrays.asList(ports1), 0, deviceId);
    GroupId gId2 = new DefaultGroupId(2);
    // Non zero reference count will make the group manager to queue
    // the extraneous groups until reference count is zero.
    Group group2 = createSouthboundGroupEntry(gId2, Arrays.asList(ports2), 2, deviceId);
    List<Group> groupEntries = Arrays.asList(group1, group2);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    Group createdGroup = groupService.getGroup(deviceId, key);

    // Group Add failure test
    GroupOperation groupAddOp =
        GroupOperation.createAddGroupOperation(
            createdGroup.id(), createdGroup.type(), createdGroup.buckets());
    providerService.groupOperationFailed(deviceId, groupAddOp);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_ADD_FAILED));

    // Group Mod failure test
    groupService.addGroup(newGroupDesc);
    createdGroup = groupService.getGroup(deviceId, key);
    assertNotNull(createdGroup);

    GroupOperation groupModOp =
        GroupOperation.createModifyGroupOperation(
            createdGroup.id(), createdGroup.type(), createdGroup.buckets());
    providerService.groupOperationFailed(deviceId, groupModOp);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATE_FAILED));

    // Group Delete failure test
    groupService.addGroup(newGroupDesc);
    createdGroup = groupService.getGroup(deviceId, key);
    assertNotNull(createdGroup);

    GroupOperation groupDelOp =
        GroupOperation.createDeleteGroupOperation(createdGroup.id(), createdGroup.type());
    providerService.groupOperationFailed(deviceId, groupDelOp);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_REMOVE_FAILED));
  }

  private Group createSouthboundGroupEntry(
      GroupId gId, List<PortNumber> ports, long referenceCount, DeviceId deviceId) {
    List<PortNumber> outPorts = new ArrayList<>();
    outPorts.addAll(ports);

    List<GroupBucket> buckets = new ArrayList<>();
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupBuckets = new GroupBuckets(buckets);
    StoredGroupEntry group = new DefaultGroup(gId, deviceId, Group.Type.SELECT, groupBuckets);
    group.setReferenceCount(referenceCount);
    return group;
  }

  private static class TestGroupListener implements GroupListener {
    final List<GroupEvent> events = new ArrayList<>();

    @Override
    public void event(GroupEvent event) {
      events.add(event);
    }

    public void validateEvent(List<GroupEvent.Type> expectedEvents) {
      int i = 0;
      System.err.println("events :" + events);
      for (GroupEvent e : events) {
        assertEquals("unexpected event", expectedEvents.get(i), e.type());
        i++;
      }
      assertEquals("mispredicted number of events", expectedEvents.size(), events.size());
      events.clear();
    }
  }

  private class TestGroupProvider extends AbstractProvider implements GroupProvider {
    DeviceId lastDeviceId;
    List<GroupOperation> groupOperations = new ArrayList<>();

    protected TestGroupProvider(ProviderId id) {
      super(id);
    }

    @Override
    public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
      lastDeviceId = deviceId;
      groupOperations.addAll(groupOps.operations());
    }

    public void validate(DeviceId expectedDeviceId, List<GroupOperation> expectedGroupOps) {
      if (expectedGroupOps == null) {
        assertTrue("events generated", groupOperations.isEmpty());
        return;
      }

      assertEquals(lastDeviceId, expectedDeviceId);
      assertTrue(
          (this.groupOperations.containsAll(expectedGroupOps)
              && expectedGroupOps.containsAll(groupOperations)));

      groupOperations.clear();
      lastDeviceId = null;
    }
  }

  private static class TestDeviceService extends DeviceServiceAdapter {
    @Override
    public int getDeviceCount() {
      return 1;
    }

    @Override
    public Iterable<Device> getDevices() {
      return ImmutableList.of(FOO_DEV);
    }

    @Override
    public Iterable<Device> getAvailableDevices() {
      return getDevices();
    }

    @Override
    public Device getDevice(DeviceId deviceId) {
      return FOO_DEV;
    }
  }

  private class TestDriverManager extends DriverManager {
    TestDriverManager() {
      this.deviceService = mgr.deviceService;
      activate();
    }
  }

  private static DeviceId lastDeviceIdProgrammable;
  private static List<GroupOperation> groupOperations = new ArrayList<>();

  public static class TestGroupProgrammable extends AbstractHandlerBehaviour
      implements GroupProgrammable {
    @Override
    public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
      lastDeviceIdProgrammable = deviceId;
      groupOperations.addAll(groupOps.operations());
    }
  }

  public void validate(DeviceId expectedDeviceId, List<GroupOperation> expectedGroupOps) {
    if (expectedGroupOps == null) {
      assertTrue("events generated", groupOperations.isEmpty());
      return;
    }

    assertEquals(lastDeviceIdProgrammable, expectedDeviceId);
    assertTrue(
        (this.groupOperations.containsAll(expectedGroupOps)
            && expectedGroupOps.containsAll(groupOperations)));

    groupOperations.clear();
    lastDeviceIdProgrammable = null;
  }
}
Exemple #10
0
  // Parses the given node with port information.
  private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
    Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
    // TL1-based ports have a name
    PortNumber port = null;
    if (node.has("name")) {
      for (Port p : deviceService.getPorts(deviceId)) {
        if (p.number().name().equals(node.get("name").asText())) {
          port = p.number();
          break;
        }
      }
    } else {
      port = portNumber(node.path("port").asLong(0));
    }

    if (port == null) {
      log.error("Cannot find port given in node {}", node);
      return null;
    }

    String portName = Strings.emptyToNull(port.name());
    SparseAnnotations annotations = null;
    if (portName != null) {
      annotations = DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, portName).build();
    }
    switch (type) {
      case COPPER:
        return new DefaultPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            type,
            node.path("speed").asLong(1_000),
            annotations);
      case FIBER:
        // Currently, assume OMS when FIBER. Provide sane defaults.
        annotations = annotations(node.get("annotations"));
        return new OmsPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            CENTER,
            CENTER.add(TOTAL),
            Frequency.ofGHz(100),
            annotations);
      case ODUCLT:
        annotations = annotations(node.get("annotations"));
        OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
        return new OduCltPortDescription(
            port, node.path("enabled").asBoolean(true), oduCltPort.signalType(), annotations);
      case OCH:
        annotations = annotations(node.get("annotations"));
        OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
        return new OchPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            ochPort.signalType(),
            ochPort.isTunable(),
            ochPort.lambda(),
            annotations);
      case OMS:
        annotations = annotations(node.get("annotations"));
        OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
        return new OmsPortDescription(
            port,
            node.path("enabled").asBoolean(true),
            omsPort.minFrequency(),
            omsPort.maxFrequency(),
            omsPort.grid(),
            annotations);
      default:
        log.warn("{}: Unsupported Port Type");
    }
    return new DefaultPortDescription(
        port,
        node.path("enabled").asBoolean(true),
        type,
        node.path("speed").asLong(1_000),
        annotations);
  }