/**
   * Create a Bridge.
   *
   * <pre>
   *
   * Example :
   *
   * Request :
   * http://localhost:8080/controller/nb/v2/networkconfig/bridgedomain/bridge/STUB/mgmt1/bridge1
   *
   * </pre>
   *
   * @param nodeType Node Type of the node with the management session.
   * @param nodeId Node Identifier of the node with the management session.
   * @param bridgeName Name / Identifier for a bridge to be created.
   * @param bridgeConfigs Additional Bridge Configurations. It takes in complex structures under the
   *     ConfigConstants.CUSTOM key. The use-cases are documented under wiki.opendaylight.org
   *     project pages: https://wiki.opendaylight.org/view/OVSDB_Integration:Mininet_OVSDB_Tutorial
   */
  @Path("/bridge/{nodeType}/{nodeId}/{bridgeName}")
  @POST
  @StatusCodes({
    @ResponseCode(code = 201, condition = "Bridge created successfully"),
    @ResponseCode(code = 404, condition = "Could not create Bridge"),
    @ResponseCode(code = 412, condition = "Failed to create Bridge due to an exception"),
    @ResponseCode(code = 503, condition = "Bridge Domain Configuration Service not available")
  })
  public Response createBridge(
      @PathParam(value = "nodeType") String nodeType,
      @PathParam(value = "nodeId") String nodeId,
      @PathParam(value = "bridgeName") String name,
      Map<String, Object> bridgeConfigs) {

    IBridgeDomainConfigService configurationService = getConfigurationService();
    if (configurationService == null) {
      throw new ServiceUnavailableException("IBridgeDomainConfigService not available.");
    }

    Node node = Node.fromString(nodeType, nodeId);
    Status status = null;
    try {
      Map<ConfigConstants, Object> configs = this.buildConfig(bridgeConfigs);
      status = configurationService.createBridgeDomain(node, name, configs);
      if (status.getCode().equals(StatusCode.SUCCESS)) {
        return Response.status(Response.Status.CREATED).build();
      }
    } catch (BridgeDomainConfigServiceException e) {
      return Response.status(Response.Status.PRECONDITION_FAILED).build();
    }
    throw new ResourceNotFoundException(status.getDescription());
  }
  @Test
  public void testFlowEntries() {
    Flow flow = new Flow();

    Match match = new Match();
    try {
      match.setField(MatchType.NW_DST, InetAddress.getByName("1.1.1.1"));
    } catch (UnknownHostException e) {
    }
    flow.setMatch(match);
    Action action = new Drop();

    List<Action> actions = new ArrayList<Action>();
    actions.add(action);
    flow.setActions(actions);
    Node node;
    try {
      // Must use a node published by the stub protocol plugin else
      // connection manager will not report it as a local node
      node = new Node("STUB", 51966);
      FlowEntry fe = new FlowEntry("g1", "f1", flow, node);
      Status stat = manager.installFlowEntry(fe);

      Assert.assertTrue(stat.getCode() == StatusCode.SUCCESS);
    } catch (ConstructionException e) {
      // Got a failure while allocating the node
      Assert.assertTrue(false);
    }
  }
  /**
   * Remove a Port from a Bridge
   *
   * <pre>
   *
   * Example :
   *
   * Request :
   * DELETE
   * http://localhost:8080/controller/nb/v2/networkconfig/bridgedomain/port/STUB/mgmt1/bridge1/port1
   *
   * </pre>
   *
   * @param nodeType Node Type of the node with the management session.
   * @param nodeId Node Identifier of the node with the management session.
   * @param bridgeName Name / Identifier of the bridge to which a Port is being added.
   * @param portName Name / Identifier of a Port that is being deleted from a bridge.
   */
  @Path("/port/{nodeType}/{nodeId}/{bridgeName}/{portName}")
  @DELETE
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @StatusCodes({
    @ResponseCode(code = 200, condition = "Port deleted successfully"),
    @ResponseCode(code = 404, condition = "Could not delete Port to the Bridge"),
    @ResponseCode(code = 412, condition = "Failed to delete Port due to an exception"),
    @ResponseCode(code = 503, condition = "Bridge Domain Configuration Service not available")
  })
  public Response deletePort(
      @PathParam(value = "nodeType") String nodeType,
      @PathParam(value = "nodeId") String nodeId,
      @PathParam(value = "bridgeName") String bridge,
      @PathParam(value = "portName") String port) {

    IBridgeDomainConfigService configurationService = getConfigurationService();
    if (configurationService == null) {
      throw new ServiceUnavailableException("IBridgeDomainConfigService not available.");
    }

    Node node = Node.fromString(nodeType, nodeId);
    Status status = null;
    try {
      status = configurationService.deletePort(node, bridge, port);
      if (status.getCode().equals(StatusCode.SUCCESS)) {
        return Response.status(Response.Status.OK).build();
      }
    } catch (Exception t) {
      return Response.status(Response.Status.PRECONDITION_FAILED).build();
    }
    throw new ResourceNotFoundException(status.getDescription());
  }
  public Status validateMeter(Meter meter) {
    String meterName;
    Status returnStatus = null;

    if (null != meter) {
      meterName = meter.getMeterName();
      if (!FRMUtil.isNameValid(meterName)) {
        logger.error("Meter Name is invalid %s" + meterName);
        returnStatus = new Status(StatusCode.BADREQUEST, "Meter Name is invalid");
        return returnStatus;
      }

      for (int i = 0; i < meter.getMeterBandHeaders().getMeterBandHeader().size(); i++) {
        if (null != meter.getFlags() && !meter.getFlags().isMeterBurst()) {
          if (0 < meter.getMeterBandHeaders().getMeterBandHeader().get(i).getBurstSize()) {
            logger.error("Burst size should only be associated when Burst FLAG is set");
            returnStatus =
                new Status(
                    StatusCode.BADREQUEST,
                    "Burst size should only be associated when Burst FLAG is set");
            break;
          }
        }
      }

      if (null != returnStatus && !returnStatus.isSuccess()) {
        return returnStatus;
      } else if (null != meter.getMeterBandHeaders()) {
        BandType setBandType = null;
        DscpRemark dscpRemark = null;
        for (int i = 0; i < meter.getMeterBandHeaders().getMeterBandHeader().size(); i++) {
          setBandType = meter.getMeterBandHeaders().getMeterBandHeader().get(i).getBandType();
          if (setBandType instanceof DscpRemark) {
            dscpRemark = (DscpRemark) setBandType;
            if (0 > dscpRemark.getRate()) {}

          } else if (setBandType instanceof Drop) {
            if (0 < dscpRemark.getPercLevel()) {
              logger.error("Number of drop Precedence level");
            }
          } else if (setBandType instanceof Experimenter) {

          }
        }
      }
    }
    return new Status(StatusCode.SUCCESS);
  }
  /**
   * Test case for the following constructors.
   *
   * <ul>
   *   <li>{@link VTNEtherMatch#VTNEtherMatch()}
   *   <li>{@link VTNEtherMatch#VTNEtherMatch(Integer)}
   *   <li>{@link VTNEtherMatch#setEtherType(Integer)}
   * </ul>
   *
   * @throws Exception An error occurred.
   */
  @Test
  public void testConstructor1() throws Exception {
    VTNEtherMatch ematch = new VTNEtherMatch();
    assertEquals(null, ematch.getSourceAddress());
    assertEquals(null, ematch.getDestinationAddress());
    assertEquals(null, ematch.getEtherType());
    assertEquals(null, ematch.getVlanId());
    assertEquals(null, ematch.getVlanPriority());
    assertEquals(true, ematch.isEmpty());

    int[] types = {
      0, 1, 0x800, 0x806, 0x86dd,
    };
    for (int type : types) {
      Integer etype = Integer.valueOf(type);
      ematch = new VTNEtherMatch(etype);
      assertEquals(null, ematch.getSourceAddress());
      assertEquals(null, ematch.getDestinationAddress());
      assertEquals(etype, ematch.getEtherType());
      assertEquals(null, ematch.getVlanId());
      assertEquals(null, ematch.getVlanPriority());
      assertEquals(false, ematch.isEmpty());

      ematch = new VTNEtherMatch();
      assertEquals(null, ematch.getEtherType());
      ematch.setEtherType(etype);
      assertEquals(etype, ematch.getEtherType());
      ematch.setEtherType(etype);
      assertEquals(etype, ematch.getEtherType());
      for (int i = 1; i <= 10; i++) {
        int t = type + i;
        try {
          ematch.setEtherType(t);
          unexpected();
        } catch (RpcException e) {
          assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
          Status st = e.getStatus();
          assertEquals(StatusCode.BADREQUEST, st.getCode());
          String msg =
              "Ethernet type conflict: type=0x"
                  + Integer.toHexString(type)
                  + ", expected=0x"
                  + Integer.toHexString(t);
          assertEquals(msg, st.getDescription());
        }
      }
    }
  }
  /**
   * Add a Port,Vlan to a Bridge
   *
   * <pre>
   *
   * Example :
   * Request :
   * http://localhost:8080/controller/nb/v2/networkconfig/bridgedomain/port/STUB/mgmt1/bridge1/port2/200
   *
   * </pre>
   *
   * @param nodeType Node Type of the node with the management session.
   * @param nodeId Node Identifier of the node with the management session.
   * @param bridgeName Name / Identifier of the bridge to which a Port is being added.
   * @param portName Name / Identifier of a Port that is being added to a bridge.
   * @param vlan Vlan Id.
   */
  @Path("/port/{nodeType}/{nodeId}/{bridgeName}/{portName}/{vlan}")
  @POST
  @StatusCodes({
    @ResponseCode(code = 201, condition = "Created Port with Vlan tag successfully"),
    @ResponseCode(code = 404, condition = "Could not add Port,Vlan to the Bridge"),
    @ResponseCode(code = 406, condition = "Invalid Vlan parameter passed."),
    @ResponseCode(code = 412, condition = "Failed to add Port,Vlan due to an exception"),
    @ResponseCode(code = 503, condition = "Bridge Domain Configuration Service not available")
  })
  public Response addPort(
      @PathParam(value = "nodeType") String nodeType,
      @PathParam(value = "nodeId") String nodeId,
      @PathParam(value = "bridgeName") String bridge,
      @PathParam(value = "portName") String port,
      @PathParam(value = "vlan") String vlan) {

    IBridgeDomainConfigService configurationService = getConfigurationService();
    if (configurationService == null) {
      throw new ServiceUnavailableException("IBridgeDomainConfigService not available.");
    }
    try {
      Integer.parseInt(vlan);
    } catch (Exception e) {
      throw new NotAcceptableException("Incorrect Vlan :" + vlan);
    }

    Node node = Node.fromString(nodeType, nodeId);
    Map<ConfigConstants, Object> configs = new HashMap<ConfigConstants, Object>();
    configs.put(ConfigConstants.TYPE, ConfigConstants.VLAN.name());
    configs.put(ConfigConstants.VLAN, vlan);

    Status status = null;
    try {
      status = configurationService.addPort(node, bridge, port, configs);
      if (status.getCode().equals(StatusCode.SUCCESS)) {
        return Response.status(Response.Status.CREATED).build();
      }
    } catch (Exception e) {
      return Response.status(Response.Status.PRECONDITION_FAILED).build();
    }
    throw new ResourceNotFoundException(status.getDescription());
  }
  @Test
  public void testStaticHost() throws UnknownHostException {
    String ip;

    assertNotNull(this.hosttracker);

    // create one node and two node connectors
    Node node1 = NodeCreator.createOFNode(1L);
    NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector((short) 1, node1);
    NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector((short) 2, node1);

    // test addStaticHost(), store into inactive host DB
    Status st = this.hosttracker.addStaticHost("192.168.0.8", "11:22:33:44:55:66", nc1_1, "0");
    Assert.assertTrue(st.isSuccess());
    st = this.hosttracker.addStaticHost("192.168.0.13", "11:22:33:44:55:77", nc1_2, "0");
    Assert.assertTrue(st.isSuccess());

    // check inactive DB
    Iterator<HostNodeConnector> hnci = this.hosttracker.getInactiveStaticHosts().iterator();
    while (hnci.hasNext()) {
      ip = hnci.next().getNetworkAddressAsString();
      Assert.assertTrue(ip.equals("192.168.0.8") || ip.equals("192.168.0.13"));
    }

    // check active host DB
    hnci = this.hosttracker.getActiveStaticHosts().iterator();
    Assert.assertFalse(hnci.hasNext());

    // test removeStaticHost()
    st = this.hosttracker.removeStaticHost("192.168.0.8");
    Assert.assertTrue(st.isSuccess());

    hnci = this.hosttracker.getInactiveStaticHosts().iterator();
    while (hnci.hasNext()) {
      ip = hnci.next().getNetworkAddressAsString();
      Assert.assertTrue(ip.equals("192.168.0.13"));
    }
  }
  /**
   * Test case for {@link VTNEtherMatch#verify()}.
   *
   * @throws Exception An error occurred.
   */
  @Test
  public void testVerify() throws Exception {
    Unmarshaller um = createUnmarshaller(VTNEtherMatch.class);

    // Invalid ether types.
    Integer[] badTypes = {
      Integer.MIN_VALUE,
      Integer.MIN_VALUE + 1,
      -0x70000000,
      -0x10000000,
      -0x10000,
      -0xffff,
      -10,
      -3,
      -2,
      -1,
      0x10000,
      0x10001,
      0x20000,
      0x10000000,
      0x50000000,
      Integer.MAX_VALUE - 2,
      Integer.MAX_VALUE - 1,
      Integer.MAX_VALUE,
    };
    for (Integer type : badTypes) {
      XmlNode root = new XmlNode(XML_ROOT).add(new XmlNode("ether-type", type));
      String xml = root.toString();
      VTNEtherMatch ematch = unmarshal(um, xml, VTNEtherMatch.class);
      try {
        ematch.verify();
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid Ethernet type: " + type, st.getDescription());
      }
    }

    // Invalid VLAN ID.
    Integer[] badVlanIds = {
      Integer.MIN_VALUE,
      Integer.MIN_VALUE + 1,
      -0x70000000,
      -0x10000000,
      -0x10000,
      -0xffff,
      -10,
      -3,
      -2,
      -1,
      0x1000,
      0x1001,
      0x1002,
      0x20000,
      0xffffff,
      0x60000000,
      Integer.MAX_VALUE - 2,
      Integer.MAX_VALUE - 1,
      Integer.MAX_VALUE,
    };
    for (Integer vid : badVlanIds) {
      XmlNode root = new XmlNode(XML_ROOT).add(new XmlNode("vlan-id", vid));
      String xml = root.toString();
      VTNEtherMatch ematch = unmarshal(um, xml, VTNEtherMatch.class);
      try {
        ematch.verify();
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid VLAN ID: " + vid, st.getDescription());
      }
    }

    // Invalid VLAN priority.
    Short[] badPcps = {
      Short.MIN_VALUE,
      -30000,
      -20000,
      -10000,
      -0x100,
      -3,
      -2,
      -1,
      8,
      9,
      10,
      0x100,
      300,
      10000,
      Short.MAX_VALUE - 1,
      Short.MAX_VALUE,
    };
    for (Short pcp : badPcps) {
      XmlNode root = new XmlNode(XML_ROOT).add(new XmlNode("vlan-pcp", pcp));
      String xml = root.toString();
      VTNEtherMatch ematch = unmarshal(um, xml, VTNEtherMatch.class);
      try {
        ematch.verify();
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid VLAN priority: " + pcp, st.getDescription());
      }
    }

    // Specifying VLAN priority without VLAN ID.
    Integer[] untagged = {null, EtherHeader.VLAN_NONE};
    for (Integer vid : untagged) {
      for (byte pcp = 0; pcp <= 7; pcp++) {
        XmlNode root = new XmlNode(XML_ROOT).add(new XmlNode("vlan-pcp", pcp));
        if (vid != null) {
          root.add(new XmlNode("vlan-id", vid));
        }
        String xml = root.toString();
        VTNEtherMatch ematch = unmarshal(um, xml, VTNEtherMatch.class);
        try {
          ematch.verify();
          unexpected();
        } catch (RpcException e) {
          assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
          Status st = e.getStatus();
          assertEquals(StatusCode.BADREQUEST, st.getCode());
          assertEquals("VLAN priority requires a valid VLAN ID.", st.getDescription());
        }
      }
    }
  }
  /**
   * Test case for the following methods.
   *
   * <ul>
   *   <li>{@link
   *       VTNEtherMatch#VTNEtherMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.cond.rev150313.VtnEtherMatchFields)}
   *   <li>{@link VTNEtherMatch#create(Match)}
   *   <li>{@link VTNEtherMatch#setMatch(MatchBuilder)}
   *   <li>Getter methods.
   *   <li>JAXB bindings.
   * </ul>
   *
   * @throws Exception An error occurred.
   */
  @Test
  public void testConstructor3() throws Exception {
    EtherAddress[] srcs = {
      null, new EtherAddress(1L), new EtherAddress(0x000102030405L),
    };
    EtherAddress[] dsts = {
      null, new EtherAddress(0xf0f1f2f3f4f5L), new EtherAddress(0xa8b9cadbecfdL),
    };
    Integer[] types = {null, 0x800, 0x86dd};
    Integer[] vlans = {null, 0, 1, 4095};
    Short[] priorities = {0, 3, 7};

    EtherMatchParams params = new EtherMatchParams();
    Class<VTNEtherMatch> mtype = VTNEtherMatch.class;
    for (EtherAddress src : srcs) {
      params.setSourceAddress(src);
      for (EtherAddress dst : dsts) {
        params.setDestinationAddress(dst);
        for (Integer type : types) {
          params.setEtherType(type);
          for (Integer vlan : vlans) {
            params.setVlanId(vlan).setVlanPriority((Short) null);
            VtnEtherMatch vem = params.toVtnEtherMatch();
            VTNEtherMatch ematch = new VTNEtherMatch(vem);
            params.verify(ematch);

            // JAXB test.
            VTNEtherMatch jaxb = jaxbTest(ematch, mtype, XML_ROOT);
            jaxb.verify();
            VtnEtherMatch vem1 = jaxb.toVtnEtherMatchBuilder().build();
            assertEquals(vem, vem1);

            if (vlan == null || vlan.intValue() == EtherHeader.VLAN_NONE) {
              continue;
            }

            for (Short pri : priorities) {
              params.setVlanPriority(pri);
              vem = params.toVtnEtherMatch();
              ematch = new VTNEtherMatch(vem);
              params.verify(ematch);

              // JAXB test.
              jaxb = jaxbTest(ematch, mtype, XML_ROOT);
              jaxb.verify();
              vem1 = jaxb.toVtnEtherMatchBuilder().build();
              assertEquals(vem, vem1);
            }
          }
        }
      }
    }

    // Invalid ether types.
    Long[] badTypes = {
      0x10000L,
      0x10001L,
      0x20000L,
      0x7fffffffL,
      0x80000000L,
      0xaaaaaaaaL,
      0xccccccccL,
      0xffff0000L,
      0xffffffffL,
    };
    for (Long type : badTypes) {
      VtnEtherMatch vem = new VtnEtherMatchBuilder().setEtherType(new EtherType(type)).build();
      try {
        new VTNEtherMatch(vem);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid Ethernet type: " + type, st.getDescription());
      }
    }

    // Specifying VLAN priority without VLAN ID.
    Integer[] untagged = {null, EtherHeader.VLAN_NONE};
    for (Integer vid : untagged) {
      params.setVlanId(vid);
      for (byte pcp = 0; pcp <= 7; pcp++) {
        params.setVlanPriority(pcp);
        VtnEtherMatch vem = params.toVtnEtherMatch();
        try {
          new VTNEtherMatch(vem);
          unexpected();
        } catch (RpcException e) {
          assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
          Status st = e.getStatus();
          assertEquals(StatusCode.BADREQUEST, st.getCode());
          assertEquals("VLAN priority requires a valid VLAN ID.", st.getDescription());
        }
      }
    }
  }
  /**
   * Test case for the following methods.
   *
   * <ul>
   *   <li>{@link VTNEtherMatch#VTNEtherMatch(EthernetMatch)}
   *   <li>{@link VTNEtherMatch#VTNEtherMatch(EtherAddress,EtherAddress,Integer,Integer,Short)}
   *   <li>{@link VTNEtherMatch#create(Match)}
   *   <li>{@link VTNEtherMatch#setMatch(MatchBuilder)}
   *   <li>Getter methods.
   * </ul>
   *
   * @throws Exception An error occurred.
   */
  @Test
  public void testConstructor2() throws Exception {
    EtherAddress[] srcs = {
      null, new EtherAddress(1L), new EtherAddress(0x000102030405L),
    };
    EtherAddress[] dsts = {
      null, new EtherAddress(0xf0f1f2f3f4f5L), new EtherAddress(0xa8b9cadbecfdL),
    };
    Integer[] types = {null, 0x800, 0x86dd};
    Integer[] vlans = {null, 0, 1, 4095};
    Short[] priorities = {0, 3, 7};

    EtherMatchParams params = new EtherMatchParams();
    for (EtherAddress src : srcs) {
      params.setSourceAddress(src);
      for (EtherAddress dst : dsts) {
        params.setDestinationAddress(dst);
        for (Integer type : types) {
          params.setEtherType(type);
          for (Integer vlan : vlans) {
            params.setVlanId(vlan).setVlanPriority((Short) null);
            VTNEtherMatch ematch = params.toVTNEtherMatch();
            params.verify(ematch);

            VTNEtherMatch ematch1 = new VTNEtherMatch(src, dst, type, vlan, null);
            assertEquals(ematch, ematch1);

            if (vlan == null || vlan.intValue() == EtherHeader.VLAN_NONE) {
              continue;
            }

            for (Short pri : priorities) {
              params.setVlanPriority(pri);
              ematch = params.toVTNEtherMatch();
              params.verify(ematch);
              ematch1 = new VTNEtherMatch(src, dst, type, vlan, pri);
              assertEquals(ematch, ematch1);
            }
          }
        }
      }
    }

    // Create broken EthernetMatch.
    String[] badAddrs = {
      "", "aa:bb:cc:dd:ee:ff:11", "00:11", "bad_address",
    };
    Unmarshaller um = createUnmarshaller(EthernetMatch.class);
    for (String addr : badAddrs) {
      String xml = new XmlNode("ethermatch").setAttribute("src", addr).toString();
      EthernetMatch em = unmarshal(um, xml, EthernetMatch.class);
      assertNotNull(em.getValidationStatus());
      try {
        new VTNEtherMatch(em);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());

        String msg = "Invalid source MAC address: " + addr + ": ";
        String desc = st.getDescription();
        assertTrue("Unexpected error message: " + desc, desc.startsWith(msg));
      }
    }

    // Invalid ether types.
    Integer[] badTypes = {
      Integer.MIN_VALUE, -10, -1, 0x10000, 0x10001, 0x33333333, Integer.MAX_VALUE,
    };
    for (Integer type : badTypes) {
      params.setEtherType(type);
      EthernetMatch em = params.toEthernetMatch();
      assertEquals(null, em.getValidationStatus());
      try {
        new VTNEtherMatch(em);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid Ethernet type: " + type, st.getDescription());
      }

      try {
        new VTNEtherMatch(null, null, type, null, null);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid Ethernet type: " + type, st.getDescription());
      }
    }

    // Invalid VLAN ID.
    params.setEtherType(0x800);
    params.setVlanPriority((Short) null);
    Short[] badVlanIds = {
      Short.MIN_VALUE, -30000, -10000, -3, -2, -1, 0x1000, 0x1001, 0x5000, Short.MAX_VALUE,
    };
    for (Short vid : badVlanIds) {
      params.setVlanId(vid);
      EthernetMatch em = params.toEthernetMatch();
      assertEquals(null, em.getValidationStatus());
      try {
        new VTNEtherMatch(em);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid VLAN ID: " + vid, st.getDescription());
      }

      try {
        new VTNEtherMatch(null, null, null, vid.intValue(), null);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid VLAN ID: " + vid, st.getDescription());
      }
    }

    // Invalid VLAN priority.
    params.setVlanId(1);
    Byte[] badPcps = {
      Byte.MIN_VALUE, -100, -50, -2, -1, 8, 9, 10, 50, 100, Byte.MAX_VALUE,
    };
    for (Byte pcp : badPcps) {
      params.setVlanPriority(pcp);
      EthernetMatch em = params.toEthernetMatch();
      assertEquals(null, em.getValidationStatus());
      try {
        new VTNEtherMatch(em);
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid VLAN priority: " + pcp, st.getDescription());
      }

      try {
        new VTNEtherMatch(null, null, null, 10, pcp.shortValue());
        unexpected();
      } catch (RpcException e) {
        assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
        Status st = e.getStatus();
        assertEquals(StatusCode.BADREQUEST, st.getCode());
        assertEquals("Invalid VLAN priority: " + pcp, st.getDescription());
      }
    }

    // Specifying VLAN priority without VLAN ID.
    Integer[] untagged = {null, EtherHeader.VLAN_NONE};
    for (Integer vid : untagged) {
      params.setVlanId(vid);
      for (byte pcp = 0; pcp <= 7; pcp++) {
        params.setVlanPriority(pcp);
        EthernetMatch em = params.toEthernetMatch();
        assertEquals(null, em.getValidationStatus());
        try {
          new VTNEtherMatch(em);
          unexpected();
        } catch (RpcException e) {
          assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
          Status st = e.getStatus();
          assertEquals(StatusCode.BADREQUEST, st.getCode());
          assertEquals("VLAN priority requires a valid VLAN ID.", st.getDescription());
        }

        try {
          new VTNEtherMatch(null, null, null, null, (short) pcp);
          unexpected();
        } catch (RpcException e) {
          assertEquals(RpcErrorTag.BAD_ELEMENT, e.getErrorTag());
          Status st = e.getStatus();
          assertEquals(StatusCode.BADREQUEST, st.getCode());
          assertEquals("VLAN priority requires a valid VLAN ID.", st.getDescription());
        }
      }
    }

    Match empty = new MatchBuilder().build();
    assertEquals(null, VTNEtherMatch.create(empty));

    // Invalid VLAN ID match.
    VlanIdBuilder vb = new VlanIdBuilder().setVlanIdPresent(true);
    VlanMatchBuilder vmb = new VlanMatchBuilder().setVlanId(vb.build());
    RpcErrorTag etag = RpcErrorTag.BAD_ELEMENT;
    StatusCode code = StatusCode.BADREQUEST;
    String msg = "Unsupported VLAN ID match: " + vmb.getVlanId();
    try {
      new VTNEtherMatch(null, vmb.build());
      unexpected();
    } catch (RpcException e) {
      assertEquals(etag, e.getErrorTag());
      Status st = e.getStatus();
      assertEquals(code, st.getCode());
      assertEquals(msg, st.getDescription());
    }

    vb.setVlanId(new VlanId(0));
    vmb.setVlanId(vb.build());
    msg = "Unsupported VLAN ID match: " + vmb.getVlanId();
    try {
      new VTNEtherMatch(null, vmb.build());
      unexpected();
    } catch (RpcException e) {
      assertEquals(etag, e.getErrorTag());
      Status st = e.getStatus();
      assertEquals(code, st.getCode());
      assertEquals(msg, st.getDescription());
    }
  }