@Override
  public void execute()
      throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
          ConcurrentOperationException, ResourceAllocationException {
    try {
      List<? extends Network> networks = _niciraNvpElementService.listNiciraNvpDeviceNetworks(this);
      ListResponse<NetworkResponse> response = new ListResponse<NetworkResponse>();
      List<NetworkResponse> networkResponses = new ArrayList<NetworkResponse>();

      if (networks != null && !networks.isEmpty()) {
        for (Network network : networks) {
          NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(network);
          networkResponses.add(networkResponse);
        }
      }

      response.setResponses(networkResponses);
      response.setResponseName(getCommandName());
      this.setResponseObject(response);
    } catch (InvalidParameterValueException invalidParamExcp) {
      throw new ServerApiException(BaseCmd.PARAM_ERROR, invalidParamExcp.getMessage());
    } catch (CloudRuntimeException runtimeExcp) {
      throw new ServerApiException(BaseCmd.INTERNAL_ERROR, runtimeExcp.getMessage());
    }
  }
  @Override
  public void execute()
      throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
          ConcurrentOperationException, ResourceAllocationException {
    try {
      List<CiscoVnmcControllerVO> ciscoVnmcResources =
          _ciscoVnmcElementService.listCiscoVnmcResources(this);
      ListResponse<CiscoVnmcResourceResponse> response =
          new ListResponse<CiscoVnmcResourceResponse>();
      List<CiscoVnmcResourceResponse> ciscoVnmcResourcesResponse =
          new ArrayList<CiscoVnmcResourceResponse>();

      if (ciscoVnmcResources != null && !ciscoVnmcResources.isEmpty()) {
        for (CiscoVnmcController ciscoVnmcResourceVO : ciscoVnmcResources) {
          CiscoVnmcResourceResponse ciscoVnmcResourceResponse =
              _ciscoVnmcElementService.createCiscoVnmcResourceResponse(ciscoVnmcResourceVO);
          ciscoVnmcResourceResponse.setObjectName("CiscoVnmcResource");
          ciscoVnmcResourcesResponse.add(ciscoVnmcResourceResponse);
        }
      }

      response.setResponses(ciscoVnmcResourcesResponse);
      response.setResponseName(getCommandName());
      this.setResponseObject(response);
    } catch (InvalidParameterValueException invalidParamExcp) {
      throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage());
    } catch (CloudRuntimeException runtimeExcp) {
      throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage());
    }
  }
  @Override
  public void execute()
      throws ResourceUnavailableException, InsufficientCapacityException,
          ResourceAllocationException {
    try {
      List<NiciraNvpDeviceVO> niciraDevices = niciraNvpElementService.listNiciraNvpDevices(this);
      ListResponse<NiciraNvpDeviceResponse> response = new ListResponse<NiciraNvpDeviceResponse>();
      List<NiciraNvpDeviceResponse> niciraDevicesResponse =
          new ArrayList<NiciraNvpDeviceResponse>();

      if (niciraDevices != null && !niciraDevices.isEmpty()) {
        for (NiciraNvpDeviceVO niciraDeviceVO : niciraDevices) {
          NiciraNvpDeviceResponse niciraDeviceResponse =
              niciraNvpElementService.createNiciraNvpDeviceResponse(niciraDeviceVO);
          niciraDevicesResponse.add(niciraDeviceResponse);
        }
      }

      response.setResponses(niciraDevicesResponse);
      response.setResponseName(getCommandName());
      setResponseObject(response);
    } catch (InvalidParameterValueException invalidParamExcp) {
      throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage());
    } catch (CloudRuntimeException runtimeExcp) {
      throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage());
    }
  }
  @Override
  public AsyncCallFuture<TemplateApiResult> copyTemplate(
      TemplateInfo srcTemplate, DataStore destStore) {
    // generate a URL from source template ssvm to download to destination data store
    String url = generateCopyUrl(srcTemplate);
    if (url == null) {
      s_logger.warn(
          "Unable to start/resume copy of template "
              + srcTemplate.getUniqueName()
              + " to "
              + destStore.getName()
              + ", no secondary storage vm in running state in source zone");
      throw new CloudRuntimeException("No secondary VM in running state in source template zone ");
    }

    TemplateObject tmplForCopy =
        (TemplateObject) _templateFactory.getTemplate(srcTemplate, destStore);
    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Setting source template url to " + url);
    }
    tmplForCopy.setUrl(url);

    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Mark template_store_ref entry as Creating");
    }
    AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<TemplateApiResult>();
    DataObject templateOnStore = destStore.create(tmplForCopy);
    templateOnStore.processEvent(Event.CreateOnlyRequested);

    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Invoke datastore driver createAsync to create template on destination store");
    }
    try {
      TemplateOpContext<TemplateApiResult> context =
          new TemplateOpContext<TemplateApiResult>(null, (TemplateObject) templateOnStore, future);
      AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller =
          AsyncCallbackDispatcher.create(this);
      caller
          .setCallback(caller.getTarget().copyTemplateCrossZoneCallBack(null, null))
          .setContext(context);
      destStore.getDriver().createAsync(destStore, templateOnStore, caller);
    } catch (CloudRuntimeException ex) {
      // clean up already persisted template_store_ref entry in case of createTemplateCallback is
      // never called
      TemplateDataStoreVO templateStoreVO =
          _vmTemplateStoreDao.findByStoreTemplate(destStore.getId(), srcTemplate.getId());
      if (templateStoreVO != null) {
        TemplateInfo tmplObj = _templateFactory.getTemplate(srcTemplate, destStore);
        tmplObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
      }
      TemplateApiResult res = new TemplateApiResult((TemplateObject) templateOnStore);
      res.setResult(ex.getMessage());
      future.complete(res);
    }
    return future;
  }
 @SuppressWarnings("deprecation")
 @Override
 public void execute() {
   try {
     Host externalFirewall = _srxElementService.addExternalFirewall(this);
     ExternalFirewallResponse response =
         _srxElementService.createExternalFirewallResponse(externalFirewall);
     response.setObjectName("externalfirewall");
     response.setResponseName(getCommandName());
     this.setResponseObject(response);
   } catch (InvalidParameterValueException ipve) {
     throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ipve.getMessage());
   } catch (CloudRuntimeException cre) {
     throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, cre.getMessage());
   }
 }
 @Override
 public void execute()
     throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
         ConcurrentOperationException, ResourceAllocationException {
   try {
     ExternalNetworkDeviceManager nwDeviceMgr;
     ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name);
     nwDeviceMgr = locator.getManager(ExternalNetworkDeviceManager.class);
     Host device = nwDeviceMgr.addNetworkDevice(this);
     NetworkDeviceResponse response = nwDeviceMgr.getApiResponse(device);
     response.setObjectName("networkdevice");
     response.setResponseName(getCommandName());
     this.setResponseObject(response);
   } catch (InvalidParameterValueException ipve) {
     throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage());
   } catch (CloudRuntimeException cre) {
     throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage());
   }
 }
  @Override
  public void createTemplateAsync(
      TemplateInfo template, DataStore store, AsyncCompletionCallback<TemplateApiResult> callback) {
    // persist template_store_ref entry
    TemplateObject templateOnStore = (TemplateObject) store.create(template);
    // update template_store_ref and template state
    try {
      templateOnStore.processEvent(ObjectInDataStoreStateMachine.Event.CreateOnlyRequested);
    } catch (Exception e) {
      TemplateApiResult result = new TemplateApiResult(templateOnStore);
      result.setResult(e.toString());
      result.setSuccess(false);
      if (callback != null) {
        callback.complete(result);
      }
      return;
    }

    try {
      TemplateOpContext<TemplateApiResult> context =
          new TemplateOpContext<TemplateApiResult>(callback, templateOnStore, null);

      AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller =
          AsyncCallbackDispatcher.create(this);
      caller.setCallback(caller.getTarget().createTemplateCallback(null, null)).setContext(context);
      store.getDriver().createAsync(store, templateOnStore, caller);
    } catch (CloudRuntimeException ex) {
      // clean up already persisted template_store_ref entry in case of createTemplateCallback is
      // never called
      TemplateDataStoreVO templateStoreVO =
          _vmTemplateStoreDao.findByStoreTemplate(store.getId(), template.getId());
      if (templateStoreVO != null) {
        TemplateInfo tmplObj = _templateFactory.getTemplate(template, store);
        tmplObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
      }
      TemplateApiResult result = new TemplateApiResult(template);
      result.setResult(ex.getMessage());
      if (callback != null) {
        callback.complete(result);
      }
    }
  }
 @Override
 public void execute()
     throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
         ConcurrentOperationException, ResourceAllocationException {
   try {
     boolean result = _ciscoVnmcElementService.deleteCiscoVnmcResource(this);
     if (result) {
       SuccessResponse response = new SuccessResponse(getCommandName());
       response.setResponseName(getCommandName());
       this.setResponseObject(response);
     } else {
       throw new ServerApiException(
           ApiErrorCode.INTERNAL_ERROR, "Failed to delete Cisco Vnmc resource.");
     }
   } catch (InvalidParameterValueException invalidParamExcp) {
     throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage());
   } catch (CloudRuntimeException runtimeExcp) {
     throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage());
   }
 }
 @Override
 public void execute()
     throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
         ConcurrentOperationException, ResourceAllocationException {
   try {
     ExternalFirewallDeviceVO fwDeviceVO = _srxFwService.addSrxFirewall(this);
     if (fwDeviceVO != null) {
       SrxFirewallResponse response = _srxFwService.createSrxFirewallResponse(fwDeviceVO);
       response.setObjectName("srxfirewall");
       response.setResponseName(getCommandName());
       this.setResponseObject(response);
     } else {
       throw new ServerApiException(
           BaseAsyncCmd.INTERNAL_ERROR, "Failed to add SRX firewall due to internal error.");
     }
   } catch (InvalidParameterValueException invalidParamExcp) {
     throw new ServerApiException(BaseCmd.PARAM_ERROR, invalidParamExcp.getMessage());
   } catch (CloudRuntimeException runtimeExcp) {
     throw new ServerApiException(BaseCmd.INTERNAL_ERROR, runtimeExcp.getMessage());
   }
 }
    @Override
    protected void runInContext() {
      // 1. Select all entries with download_state = Not_Downloaded or Download_In_Progress
      // 2. Get corresponding volume
      // 3. Get EP using _epSelector
      // 4. Check if SSVM is owned by this MS
      // 5. If owned by MS then send command to appropriate SSVM
      // 6. In listener check for the answer and update DB accordingly
      List<VolumeDataStoreVO> volumeDataStores =
          _volumeDataStoreDao.listByVolumeState(
              Volume.State.NotUploaded, Volume.State.UploadInProgress);
      for (VolumeDataStoreVO volumeDataStore : volumeDataStores) {
        try {
          DataStore dataStore =
              storeMgr.getDataStore(volumeDataStore.getDataStoreId(), DataStoreRole.Image);
          EndPoint ep = _epSelector.select(dataStore, volumeDataStore.getExtractUrl());
          if (ep == null) {
            s_logger.warn(
                "There is no secondary storage VM for image store " + dataStore.getName());
            continue;
          }
          VolumeVO volume = _volumeDao.findById(volumeDataStore.getVolumeId());
          if (volume == null) {
            s_logger.warn("Volume with id " + volumeDataStore.getVolumeId() + " not found");
            continue;
          }
          Host host = _hostDao.findById(ep.getId());
          UploadStatusCommand cmd = new UploadStatusCommand(volume.getUuid(), EntityType.Volume);
          if (host != null && host.getManagementServerId() != null) {
            if (_nodeId == host.getManagementServerId().longValue()) {
              Answer answer = null;
              try {
                answer = ep.sendMessage(cmd);
              } catch (CloudRuntimeException e) {
                s_logger.warn(
                    "Unable to get upload status for volume "
                        + volume.getUuid()
                        + ". Error details: "
                        + e.getMessage());
                answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage());
              }
              if (answer == null || !(answer instanceof UploadStatusAnswer)) {
                s_logger.warn(
                    "No or invalid answer corresponding to UploadStatusCommand for volume "
                        + volumeDataStore.getVolumeId());
                continue;
              }
              handleVolumeStatusResponse((UploadStatusAnswer) answer, volume, volumeDataStore);
            }
          } else {
            String error =
                "Volume "
                    + volume.getUuid()
                    + " failed to upload as SSVM is either destroyed or SSVM agent not in 'Up' state";
            handleVolumeStatusResponse(
                new UploadStatusAnswer(cmd, UploadStatus.ERROR, error), volume, volumeDataStore);
          }
        } catch (Throwable th) {
          s_logger.warn(
              "Exception while checking status for uploaded volume "
                  + volumeDataStore.getExtractUrl()
                  + ". Error details: "
                  + th.getMessage());
          if (s_logger.isTraceEnabled()) {
            s_logger.trace("Exception details: ", th);
          }
        }
      }

      // Handle for template upload as well
      List<TemplateDataStoreVO> templateDataStores =
          _templateDataStoreDao.listByTemplateState(
              VirtualMachineTemplate.State.NotUploaded,
              VirtualMachineTemplate.State.UploadInProgress);
      for (TemplateDataStoreVO templateDataStore : templateDataStores) {
        try {
          DataStore dataStore =
              storeMgr.getDataStore(templateDataStore.getDataStoreId(), DataStoreRole.Image);
          EndPoint ep = _epSelector.select(dataStore, templateDataStore.getExtractUrl());
          if (ep == null) {
            s_logger.warn(
                "There is no secondary storage VM for image store " + dataStore.getName());
            continue;
          }
          VMTemplateVO template = _templateDao.findById(templateDataStore.getTemplateId());
          if (template == null) {
            s_logger.warn("Template with id " + templateDataStore.getTemplateId() + " not found");
            continue;
          }
          Host host = _hostDao.findById(ep.getId());
          UploadStatusCommand cmd =
              new UploadStatusCommand(template.getUuid(), EntityType.Template);
          if (host != null && host.getManagementServerId() != null) {
            if (_nodeId == host.getManagementServerId().longValue()) {
              Answer answer = null;
              try {
                answer = ep.sendMessage(cmd);
              } catch (CloudRuntimeException e) {
                s_logger.warn(
                    "Unable to get upload status for template "
                        + template.getUuid()
                        + ". Error details: "
                        + e.getMessage());
                answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage());
              }
              if (answer == null || !(answer instanceof UploadStatusAnswer)) {
                s_logger.warn(
                    "No or invalid answer corresponding to UploadStatusCommand for template "
                        + templateDataStore.getTemplateId());
                continue;
              }
              handleTemplateStatusResponse(
                  (UploadStatusAnswer) answer, template, templateDataStore);
            }
          } else {
            String error =
                "Template "
                    + template.getUuid()
                    + " failed to upload as SSVM is either destroyed or SSVM agent not in 'Up' state";
            handleTemplateStatusResponse(
                new UploadStatusAnswer(cmd, UploadStatus.ERROR, error),
                template,
                templateDataStore);
          }
        } catch (Throwable th) {
          s_logger.warn(
              "Exception while checking status for uploaded template "
                  + templateDataStore.getExtractUrl()
                  + ". Error details: "
                  + th.getMessage());
          if (s_logger.isTraceEnabled()) {
            s_logger.trace("Exception details: ", th);
          }
        }
      }
    }
  @Override
  public Answer execute(
      final BackupSnapshotCommand command,
      final LibvirtComputingResource libvirtComputingResource) {
    final Long dcId = command.getDataCenterId();
    final Long accountId = command.getAccountId();
    final Long volumeId = command.getVolumeId();
    final String secondaryStoragePoolUrl = command.getSecondaryStorageUrl();
    final String snapshotName = command.getSnapshotName();
    String snapshotDestPath = null;
    String snapshotRelPath = null;
    final String vmName = command.getVmName();
    KVMStoragePool secondaryStoragePool = null;
    final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();

    try {
      final LibvirtUtilitiesHelper libvirtUtilitiesHelper =
          libvirtComputingResource.getLibvirtUtilitiesHelper();
      final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);

      secondaryStoragePool = storagePoolMgr.getStoragePoolByURI(secondaryStoragePoolUrl);

      final String ssPmountPath = secondaryStoragePool.getLocalPath();
      snapshotRelPath =
          File.separator
              + "snapshots"
              + File.separator
              + dcId
              + File.separator
              + accountId
              + File.separator
              + volumeId;

      snapshotDestPath =
          ssPmountPath
              + File.separator
              + "snapshots"
              + File.separator
              + dcId
              + File.separator
              + accountId
              + File.separator
              + volumeId;
      final KVMStoragePool primaryPool =
          storagePoolMgr.getStoragePool(
              command.getPool().getType(), command.getPrimaryStoragePoolNameLabel());
      final KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(command.getVolumePath());

      final String manageSnapshotPath = libvirtComputingResource.manageSnapshotPath();
      final int cmdsTimeout = libvirtComputingResource.getCmdsTimeout();

      /**
       * RBD snapshots can't be copied using qemu-img, so we have to use the Java bindings for
       * librbd here.
       *
       * <p>These bindings will read the snapshot and write the contents to the secondary storage
       * directly
       *
       * <p>It will stop doing so if the amount of time spend is longer then cmds.timeout
       */
      if (primaryPool.getType() == StoragePoolType.RBD) {
        try {
          final Rados r = new Rados(primaryPool.getAuthUserName());
          r.confSet("mon_host", primaryPool.getSourceHost() + ":" + primaryPool.getSourcePort());
          r.confSet("key", primaryPool.getAuthSecret());
          r.confSet("client_mount_timeout", "30");
          r.connect();
          s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));

          final IoCTX io = r.ioCtxCreate(primaryPool.getSourceDir());
          final Rbd rbd = new Rbd(io);
          final RbdImage image = rbd.open(snapshotDisk.getName(), snapshotName);
          final File fh = new File(snapshotDestPath);
          try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fh)); ) {
            final int chunkSize = 4194304;
            long offset = 0;
            s_logger.debug(
                "Backuping up RBD snapshot " + snapshotName + " to  " + snapshotDestPath);
            while (true) {
              final byte[] buf = new byte[chunkSize];
              final int bytes = image.read(offset, buf, chunkSize);
              if (bytes <= 0) {
                break;
              }
              bos.write(buf, 0, bytes);
              offset += bytes;
            }
            s_logger.debug(
                "Completed backing up RBD snapshot "
                    + snapshotName
                    + " to  "
                    + snapshotDestPath
                    + ". Bytes written: "
                    + offset);
          } catch (final IOException ex) {
            s_logger.error("BackupSnapshotAnswer:Exception:" + ex.getMessage());
          }
          r.ioCtxDestroy(io);
        } catch (final RadosException e) {
          s_logger.error("A RADOS operation failed. The error was: " + e.getMessage());
          return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
        } catch (final RbdException e) {
          s_logger.error(
              "A RBD operation on "
                  + snapshotDisk.getName()
                  + " failed. The error was: "
                  + e.getMessage());
          return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
        }
      } else {
        final Script scriptCommand = new Script(manageSnapshotPath, cmdsTimeout, s_logger);
        scriptCommand.add("-b", snapshotDisk.getPath());
        scriptCommand.add("-n", snapshotName);
        scriptCommand.add("-p", snapshotDestPath);
        scriptCommand.add("-t", snapshotName);
        final String result = scriptCommand.execute();

        if (result != null) {
          s_logger.debug("Failed to backup snaptshot: " + result);
          return new BackupSnapshotAnswer(command, false, result, null, true);
        }
      }
      /* Delete the snapshot on primary */

      DomainState state = null;
      Domain vm = null;
      if (vmName != null) {
        try {
          vm = libvirtComputingResource.getDomain(conn, command.getVmName());
          state = vm.getInfo().state;
        } catch (final LibvirtException e) {
          s_logger.trace("Ignoring libvirt error.", e);
        }
      }

      final KVMStoragePool primaryStorage =
          storagePoolMgr.getStoragePool(command.getPool().getType(), command.getPool().getUuid());

      if (state == DomainState.VIR_DOMAIN_RUNNING && !primaryStorage.isExternalSnapshot()) {
        final MessageFormat snapshotXML =
            new MessageFormat(
                "   <domainsnapshot>"
                    + "       <name>{0}</name>"
                    + "          <domain>"
                    + "            <uuid>{1}</uuid>"
                    + "        </domain>"
                    + "    </domainsnapshot>");

        final String vmUuid = vm.getUUIDString();
        final Object[] args = new Object[] {snapshotName, vmUuid};
        final String snapshot = snapshotXML.format(args);
        s_logger.debug(snapshot);
        final DomainSnapshot snap = vm.snapshotLookupByName(snapshotName);
        if (snap != null) {
          snap.delete(0);
        } else {
          throw new CloudRuntimeException("Unable to find vm snapshot with name -" + snapshotName);
        }

        /*
         * libvirt on RHEL6 doesn't handle resume event emitted from
         * qemu
         */
        vm = libvirtComputingResource.getDomain(conn, command.getVmName());
        state = vm.getInfo().state;
        if (state == DomainState.VIR_DOMAIN_PAUSED) {
          vm.resume();
        }
      } else {
        final Script scriptCommand = new Script(manageSnapshotPath, cmdsTimeout, s_logger);
        scriptCommand.add("-d", snapshotDisk.getPath());
        scriptCommand.add("-n", snapshotName);
        final String result = scriptCommand.execute();
        if (result != null) {
          s_logger.debug("Failed to backup snapshot: " + result);
          return new BackupSnapshotAnswer(
              command, false, "Failed to backup snapshot: " + result, null, true);
        }
      }
    } catch (final LibvirtException e) {
      return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
    } catch (final CloudRuntimeException e) {
      return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
    } finally {
      if (secondaryStoragePool != null) {
        storagePoolMgr.deleteStoragePool(
            secondaryStoragePool.getType(), secondaryStoragePool.getUuid());
      }
    }
    return new BackupSnapshotAnswer(
        command, true, null, snapshotRelPath + File.separator + snapshotName, true);
  }
  @Override
  public AsyncCallFuture<TemplateApiResult> copyTemplate(
      TemplateInfo srcTemplate, DataStore destStore) {
    // for vmware template, we need to check if ova packing is needed, since template created from
    // snapshot does not have .ova file
    // we invoke createEntityExtractURL to trigger ova packing. Ideally, we can directly use
    // extractURL to pass to following createTemplate.
    // Need to understand what is the background to use two different urls for copy and extract.
    if (srcTemplate.getFormat() == ImageFormat.OVA) {
      ImageStoreEntity tmpltStore = (ImageStoreEntity) srcTemplate.getDataStore();
      tmpltStore.createEntityExtractUrl(
          srcTemplate.getInstallPath(), srcTemplate.getFormat(), srcTemplate);
    }
    // generate a URL from source template ssvm to download to destination data store
    String url = generateCopyUrl(srcTemplate);
    if (url == null) {
      s_logger.warn(
          "Unable to start/resume copy of template "
              + srcTemplate.getUniqueName()
              + " to "
              + destStore.getName()
              + ", no secondary storage vm in running state in source zone");
      throw new CloudRuntimeException("No secondary VM in running state in source template zone ");
    }

    TemplateObject tmplForCopy =
        (TemplateObject) _templateFactory.getTemplate(srcTemplate, destStore);
    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Setting source template url to " + url);
    }
    tmplForCopy.setUrl(url);

    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Mark template_store_ref entry as Creating");
    }
    AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<TemplateApiResult>();
    DataObject templateOnStore = destStore.create(tmplForCopy);
    templateOnStore.processEvent(Event.CreateOnlyRequested);

    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Invoke datastore driver createAsync to create template on destination store");
    }
    try {
      TemplateOpContext<TemplateApiResult> context =
          new TemplateOpContext<TemplateApiResult>(null, (TemplateObject) templateOnStore, future);
      AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller =
          AsyncCallbackDispatcher.create(this);
      caller
          .setCallback(caller.getTarget().copyTemplateCrossZoneCallBack(null, null))
          .setContext(context);
      destStore.getDriver().createAsync(destStore, templateOnStore, caller);
    } catch (CloudRuntimeException ex) {
      // clean up already persisted template_store_ref entry in case of createTemplateCallback is
      // never called
      TemplateDataStoreVO templateStoreVO =
          _vmTemplateStoreDao.findByStoreTemplate(destStore.getId(), srcTemplate.getId());
      if (templateStoreVO != null) {
        TemplateInfo tmplObj = _templateFactory.getTemplate(srcTemplate, destStore);
        tmplObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
      }
      TemplateApiResult res = new TemplateApiResult((TemplateObject) templateOnStore);
      res.setResult(ex.getMessage());
      future.complete(res);
    }
    return future;
  }