private void attachCluster(
      final APIAttachPrimaryStorageToClusterMsg msg, final NoErrorCompletion completion) {
    final APIAttachPrimaryStorageToClusterEvent evt =
        new APIAttachPrimaryStorageToClusterEvent(msg.getId());
    try {
      extpEmitter.preAttach(self, msg.getClusterUuid());
    } catch (PrimaryStorageException pe) {
      evt.setErrorCode(
          errf.instantiateErrorCode(PrimaryStorageErrors.ATTACH_ERROR, pe.getMessage()));
      bus.publish(evt);
      completion.done();
      return;
    }

    extpEmitter.beforeAttach(self, msg.getClusterUuid());
    attachHook(
        msg.getClusterUuid(),
        new Completion(msg, completion) {
          @Override
          public void success() {
            PrimaryStorageClusterRefVO ref = new PrimaryStorageClusterRefVO();
            ref.setClusterUuid(msg.getClusterUuid());
            ref.setPrimaryStorageUuid(self.getUuid());
            dbf.persist(ref);

            self = dbf.reload(self);
            extpEmitter.afterAttach(self, msg.getClusterUuid());

            PrimaryStorageInventory pinv = (PrimaryStorageInventory) invf.valueOf(self);
            evt.setInventory(pinv);
            logger.debug(
                String.format(
                    "successfully attached primary storage[name:%s, uuid:%s]",
                    pinv.getName(), pinv.getUuid()));
            bus.publish(evt);
            completion.done();
          }

          @Override
          public void fail(ErrorCode errorCode) {
            extpEmitter.failToAttach(self, msg.getClusterUuid());
            evt.setErrorCode(
                errf.instantiateErrorCode(PrimaryStorageErrors.ATTACH_ERROR, errorCode));
            bus.publish(evt);
            completion.done();
          }
        });
  }
  protected void handle(final APIDetachPrimaryStorageFromClusterMsg msg) {
    final APIDetachPrimaryStorageFromClusterEvent evt =
        new APIDetachPrimaryStorageFromClusterEvent(msg.getId());

    try {
      extpEmitter.preDetach(self, msg.getClusterUuid());
    } catch (PrimaryStorageException e) {
      evt.setErrorCode(
          errf.instantiateErrorCode(PrimaryStorageErrors.DETACH_ERROR, e.getMessage()));
      bus.publish(evt);
      return;
    }

    String issuer = PrimaryStorageVO.class.getSimpleName();
    List<PrimaryStorageDetachStruct> ctx = new ArrayList<PrimaryStorageDetachStruct>();
    PrimaryStorageDetachStruct struct = new PrimaryStorageDetachStruct();
    struct.setClusterUuid(msg.getClusterUuid());
    struct.setPrimaryStorageUuid(msg.getPrimaryStorageUuid());
    ctx.add(struct);
    casf.asyncCascade(
        PrimaryStorageConstant.PRIMARY_STORAGE_DETACH_CODE,
        issuer,
        ctx,
        new Completion(msg) {
          @Override
          public void success() {
            self = dbf.reload(self);
            evt.setInventory(PrimaryStorageInventory.valueOf(self));
            bus.publish(evt);
          }

          @Override
          public void fail(ErrorCode errorCode) {
            evt.setErrorCode(errorCode);
            bus.publish(evt);
          }
        });
  }
  protected void handle(APIChangePrimaryStorageStateMsg msg) {
    APIChangePrimaryStorageStateEvent evt = new APIChangePrimaryStorageStateEvent(msg.getId());

    PrimaryStorageState currState = self.getState();
    PrimaryStorageStateEvent event = PrimaryStorageStateEvent.valueOf(msg.getStateEvent());
    PrimaryStorageState nextState = AbstractPrimaryStorage.getNextState(currState, event);

    try {
      extpEmitter.preChange(self, event);
    } catch (PrimaryStorageException e) {
      evt.setErrorCode(
          errf.instantiateErrorCode(SysErrors.CHANGE_RESOURCE_STATE_ERROR, e.getMessage()));
      bus.publish(evt);
      return;
    }

    extpEmitter.beforeChange(self, event);
    changeStateHook(event, nextState);
    self.setState(nextState);
    self = dbf.updateAndRefresh(self);
    extpEmitter.afterChange(self, event, currState);
    evt.setInventory(PrimaryStorageInventory.valueOf(self));
    bus.publish(evt);
  }