@Override
  public void applyEip(final EipStruct struct, final Completion completion) {
    L3NetworkVO l3vo = dbf.findByUuid(struct.getNic().getL3NetworkUuid(), L3NetworkVO.class);
    final L3NetworkInventory l3inv = L3NetworkInventory.valueOf(l3vo);
    vrMgr.acquireVirtualRouterVm(
        l3inv,
        new VirtualRouterOfferingValidator() {
          @Override
          public void validate(VirtualRouterOfferingInventory offering)
              throws OperationFailureException {
            if (!offering.getPublicNetworkUuid().equals(struct.getVip().getL3NetworkUuid())) {
              throw new OperationFailureException(
                  errf.stringToOperationError(
                      String.format(
                          "found a virtual router offering[uuid:%s] for L3Network[uuid:%s] in zone[uuid:%s]; however, the network's public network[uuid:%s] is not the same to EIP[uuid:%s]'s; you may need to use system tag"
                              + " guestL3Network::l3NetworkUuid to specify a particular virtual router offering for the L3Network",
                          offering.getUuid(),
                          l3inv.getUuid(),
                          l3inv.getZoneUuid(),
                          struct.getVip().getL3NetworkUuid(),
                          struct.getEip().getUuid())));
            }
          }
        },
        new ReturnValueCompletion<VirtualRouterVmInventory>(completion) {
          @Override
          public void success(final VirtualRouterVmInventory vr) {
            applyEip(
                vr,
                struct,
                new Completion() {
                  @Override
                  public void success() {
                    SimpleQuery<VirtualRouterEipRefVO> q =
                        dbf.createQuery(VirtualRouterEipRefVO.class);
                    q.add(VirtualRouterEipRefVO_.eipUuid, Op.EQ, struct.getEip().getUuid());
                    if (!q.isExists()) {
                      // if virtual router is stopped outside zstack (e.g. the host rebbot)
                      // database will still have VirtualRouterEipRefVO for this EIP.
                      // in this case, don't create the record again
                      VirtualRouterEipRefVO ref = new VirtualRouterEipRefVO();
                      ref.setEipUuid(struct.getEip().getUuid());
                      ref.setVirtualRouterVmUuid(vr.getUuid());
                      dbf.persist(ref);
                    }
                    completion.success();
                  }

                  @Override
                  public void fail(ErrorCode errorCode) {
                    completion.fail(errorCode);
                  }
                });
          }

          @Override
          public void fail(ErrorCode errorCode) {
            completion.fail(errorCode);
          }
        });
  }