This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch 4.19
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit c779b1c616471b8fea833c4b090a7f3bc2dc6ab7
Merge: 91c7bc722f2 2339412f734
Author: Daan Hoogland <d...@onecht.net>
AuthorDate: Thu Jun 6 11:24:09 2024 +0200

    Merge branch '4.18' into 4.19

 .../kvm/storage/LinstorStorageAdaptor.java         |   24 +-
 .../driver/LinstorPrimaryDataStoreDriverImpl.java  |   30 +-
 .../storage/datastore/util/LinstorUtil.java        |   33 +
 .../com/cloud/sample/UserCloudAPIExecutor.java     |  188 --
 .../com/cloud/test/longrun/BuildGuestNetwork.java  |  123 --
 .../java/com/cloud/test/longrun/GuestNetwork.java  |  107 -
 .../com/cloud/test/longrun/PerformanceWithAPI.java |  190 --
 .../main/java/com/cloud/test/longrun/User.java     |  202 --
 .../com/cloud/test/longrun/VirtualMachine.java     |   95 -
 .../java/com/cloud/test/regression/ApiCommand.java |  848 --------
 .../java/com/cloud/test/regression/ConfigTest.java |  125 --
 .../cloud/test/regression/DelegatedAdminTest.java  |  129 --
 .../java/com/cloud/test/regression/Deploy.java     |  109 -
 .../com/cloud/test/regression/EventsApiTest.java   |  176 --
 .../main/java/com/cloud/test/regression/HA.java    |   80 -
 .../cloud/test/regression/LoadBalancingTest.java   |  142 --
 .../cloud/test/regression/PortForwardingTest.java  |  143 --
 .../java/com/cloud/test/regression/SanityTest.java |   86 -
 .../main/java/com/cloud/test/regression/Test.java  |   88 -
 .../com/cloud/test/regression/TestCaseEngine.java  |  275 ---
 .../java/com/cloud/test/regression/VMApiTest.java  |   91 -
 .../main/java/com/cloud/test/stress/SshTest.java   |   90 -
 .../cloud/test/stress/StressTestDirectAttach.java  | 1353 ------------
 .../com/cloud/test/stress/TestClientWithAPI.java   | 2289 --------------------
 .../main/java/com/cloud/test/stress/WgetTest.java  |  150 --
 .../cloud/test/ui/AbstractSeleniumTestCase.java    |   55 -
 .../java/com/cloud/test/ui/AddAndDeleteAISO.java   |  127 --
 .../com/cloud/test/ui/AddAndDeleteATemplate.java   |  126 --
 .../java/com/cloud/test/ui/UIScenarioTest.java     |   86 -
 .../java/com/cloud/test/utils/ConsoleProxy.java    |  110 -
 .../java/com/cloud/test/utils/IpSqlGenerator.java  |   89 -
 .../java/com/cloud/test/utils/ProxyLoadTemp.java   |  112 -
 .../main/java/com/cloud/test/utils/SignEC2.java    |  143 --
 .../java/com/cloud/test/utils/SignRequest.java     |  112 -
 .../com/cloud/test/utils/SqlDataGenerator.java     |   49 -
 .../main/java/com/cloud/test/utils/SubmitCert.java |  198 --
 .../main/java/com/cloud/test/utils/TestClient.java |  385 ----
 .../java/com/cloud/test/utils/UtilsForTest.java    |  210 --
 ui/public/locales/en.json                          |    2 +-
 ui/src/components/page/GlobalFooter.vue            |    2 +-
 40 files changed, 46 insertions(+), 8926 deletions(-)

diff --cc 
plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
index 9612781ee4c,8b9b768d2a4..bc5b9b6a0e2
--- 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
@@@ -28,8 -28,6 +28,7 @@@ import com.linbit.linstor.api.model.Res
  import com.linbit.linstor.api.model.ResourceDefinitionCreate;
  import com.linbit.linstor.api.model.ResourceDefinitionModify;
  import com.linbit.linstor.api.model.ResourceGroupSpawn;
 +import com.linbit.linstor.api.model.ResourceMakeAvailable;
- import com.linbit.linstor.api.model.ResourceWithVolumes;
  import com.linbit.linstor.api.model.Snapshot;
  import com.linbit.linstor.api.model.SnapshotRestore;
  import com.linbit.linstor.api.model.VolumeDefinition;
@@@ -441,32 -393,13 +421,32 @@@ public class LinstorPrimaryDataStoreDri
          try
          {
              s_logger.info("Linstor: Spawn resource " + rscName);
 -            ApiCallRcList answers = linstorApi.resourceGroupSpawn(rscGrp, 
rscGrpSpawn);
 +            ApiCallRcList answers = api.resourceGroupSpawn(rscGrp, 
rscGrpSpawn);
              checkLinstorAnswersThrow(answers);
  
 -            applyAuxProps(linstorApi, rscName, vol.getName(), 
vol.getAttachedVmName());
 +            applyAuxProps(api, rscName, volName, vmName);
 +
-             return getDeviceName(api, rscName);
++            return LinstorUtil.getDevicePath(api, rscName);
 +        } catch (ApiException apiEx)
 +        {
 +            s_logger.error("Linstor: ApiEx - " + apiEx.getMessage());
 +            throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
 +        }
 +    }
 +
 +    private String createResource(VolumeInfo vol, StoragePoolVO 
storagePoolVO) {
 +        DevelopersApi linstorApi = 
LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress());
 +        final String rscGrp = getRscGrp(storagePoolVO);
 +
 +        final String rscName = LinstorUtil.RSC_PREFIX + vol.getUuid();
 +        String deviceName = createResourceBase(
 +            rscName, vol.getSize(), vol.getName(), vol.getAttachedVmName(), 
linstorApi, rscGrp);
 +
 +        try
 +        {
              applyQoSSettings(storagePoolVO, linstorApi, rscName, 
vol.getMaxIops());
  
-             return deviceName;
+             return LinstorUtil.getDevicePath(linstorApi, rscName);
          } catch (ApiException apiEx)
          {
              s_logger.error("Linstor: ApiEx - " + apiEx.getMessage());
@@@ -833,267 -676,10 +813,267 @@@
      }
  
      @Override
 -    public void copyAsync(DataObject srcData, DataObject destData, 
AsyncCompletionCallback<CopyCommandResult> callback)
 +    public void copyAsync(DataObject srcData, DataObject dstData, 
AsyncCompletionCallback<CopyCommandResult> callback)
      {
 -        // as long as canCopy is false, this isn't called
 -        s_logger.debug("Linstor: copyAsync with srcdata: " + 
srcData.getUuid());
 +        s_logger.debug("LinstorPrimaryDataStoreDriverImpl.copyAsync: "
 +            + srcData.getType() + " -> " + dstData.getType());
 +
 +        final CopyCommandResult res;
 +        if (canCopySnapshotCond(srcData, dstData)) {
 +            String errMsg = null;
 +            Answer answer = copySnapshot(srcData, dstData);
 +            if (answer != null && !answer.getResult()) {
 +                errMsg = answer.getDetails();
 +            } else {
 +                // delete primary storage snapshot
 +                SnapshotInfo sinfo = (SnapshotInfo) srcData;
 +                VolumeInfo volume = sinfo.getBaseVolume();
 +                deleteSnapshot(
 +                    srcData.getDataStore(),
 +                    LinstorUtil.RSC_PREFIX + volume.getUuid(),
 +                    LinstorUtil.RSC_PREFIX + sinfo.getUuid());
 +            }
 +            res = new CopyCommandResult(null, answer);
 +            res.setResult(errMsg);
 +        } else if (canCopyTemplateCond(srcData, dstData)) {
 +            Answer answer = copyTemplate(srcData, dstData);
 +            res = new CopyCommandResult(null, answer);
 +        } else if (canCopyVolumeCond(srcData, dstData)) {
 +            Answer answer = copyVolume(srcData, dstData);
 +            res = new CopyCommandResult(null, answer);
 +        } else {
 +            Answer answer = new Answer(null, false, "noimpl");
 +            res = new CopyCommandResult(null, answer);
 +            res.setResult("Not implemented yet");
 +        }
 +        callback.complete(res);
 +    }
 +
 +    /**
 +     * Tries to get a Linstor cloudstack end point, that is at least diskless.
 +     *
 +     * @param api Linstor java api object
 +     * @param rscName resource name to make available on node
 +     * @return Optional RemoteHostEndPoint if one could get found.
 +     * @throws ApiException
 +     */
 +    private Optional<RemoteHostEndPoint> getLinstorEP(DevelopersApi api, 
String rscName) throws ApiException {
 +        List<String> linstorNodeNames = LinstorUtil.getLinstorNodeNames(api);
 +        Collections.shuffle(linstorNodeNames);  // do not always pick the 
first linstor node
 +
 +        Host host = null;
 +        for (String nodeName : linstorNodeNames) {
 +            host = _hostDao.findByName(nodeName);
 +            if (host != null && host.getResourceState() == 
ResourceState.Enabled) {
 +                s_logger.info(String.format("Linstor: Make resource %s 
available on node %s ...", rscName, nodeName));
 +                ApiCallRcList answers = 
api.resourceMakeAvailableOnNode(rscName, nodeName, new ResourceMakeAvailable());
 +                if (!answers.hasError()) {
 +                    break; // found working host
 +                } else {
 +                    s_logger.error(
 +                        String.format("Linstor: Unable to make resource %s on 
node %s available: %s",
 +                            rscName,
 +                            nodeName,
 +                            LinstorUtil.getBestErrorMessage(answers)));
 +                }
 +            }
 +        }
 +
 +        if (host == null)
 +        {
 +            s_logger.error("Linstor: Couldn't create a resource on any 
cloudstack host.");
 +            return Optional.empty();
 +        }
 +        else
 +        {
 +            return 
Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
 +        }
 +    }
 +
 +    private Optional<RemoteHostEndPoint> getDiskfullEP(DevelopersApi api, 
String rscName) throws ApiException {
 +        List<com.linbit.linstor.api.model.StoragePool> linSPs = 
LinstorUtil.getDiskfulStoragePools(api, rscName);
 +        if (linSPs != null) {
 +            for (com.linbit.linstor.api.model.StoragePool sp : linSPs) {
 +                Host host = _hostDao.findByName(sp.getNodeName());
 +                if (host != null && host.getResourceState() == 
ResourceState.Enabled) {
 +                    return 
Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
 +                }
 +            }
 +        }
 +        s_logger.error("Linstor: No diskfull host found.");
 +        return Optional.empty();
 +    }
 +
 +    private String restoreResourceFromSnapshot(
 +            DevelopersApi api,
 +            StoragePoolVO storagePoolVO,
 +            String rscName,
 +            String snapshotName,
 +            String restoredName) throws ApiException {
 +        final String rscGrp = getRscGrp(storagePoolVO);
 +        ResourceDefinitionCreate rdc = 
createResourceDefinitionCreate(restoredName, rscGrp);
 +        api.resourceDefinitionCreate(rdc);
 +
 +        SnapshotRestore sr = new SnapshotRestore();
 +        sr.toResource(restoredName);
 +        api.resourceSnapshotsRestoreVolumeDefinition(rscName, snapshotName, 
sr);
 +
 +        api.resourceSnapshotRestore(rscName, snapshotName, sr);
 +
-         return getDeviceName(api, restoredName);
++        return LinstorUtil.getDevicePath(api, restoredName);
 +    }
 +
 +    private Answer copyTemplate(DataObject srcData, DataObject dstData) {
 +        TemplateInfo tInfo = (TemplateInfo) dstData;
 +        final StoragePoolVO pool = 
_storagePoolDao.findById(dstData.getDataStore().getId());
 +        final DevelopersApi api = 
LinstorUtil.getLinstorAPI(pool.getHostAddress());
 +        final String rscName = LinstorUtil.RSC_PREFIX + dstData.getUuid();
 +        createResourceBase(
 +            LinstorUtil.RSC_PREFIX + dstData.getUuid(),
 +            tInfo.getSize(),
 +            tInfo.getName(),
 +            "",
 +            api,
 +            getRscGrp(pool));
 +
 +        int nMaxExecutionMinutes = NumbersUtil.parseInt(
 +            _configDao.getValue(Config.SecStorageCmdExecutionTimeMax.key()), 
30);
 +        CopyCommand cmd = new CopyCommand(
 +            srcData.getTO(),
 +            dstData.getTO(),
 +            nMaxExecutionMinutes * 60 * 1000,
 +            VirtualMachineManager.ExecuteInSequence.value());
 +        Answer answer;
 +
 +        try {
 +            Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, rscName);
 +            if (optEP.isPresent()) {
 +                answer = optEP.get().sendMessage(cmd);
 +            }
 +            else {
 +                answer = new Answer(cmd, false, "Unable to get matching 
Linstor endpoint.");
 +                deleteResourceDefinition(pool, rscName);
 +            }
 +        } catch (ApiException exc) {
 +            s_logger.error("copy template failed: ", exc);
 +            deleteResourceDefinition(pool, rscName);
 +            throw new CloudRuntimeException(exc.getBestMessage());
 +        }
 +        return answer;
 +    }
 +
 +    private Answer copyVolume(DataObject srcData, DataObject dstData) {
 +        VolumeInfo srcVolInfo = (VolumeInfo) srcData;
 +        final StoragePoolVO pool = 
_storagePoolDao.findById(srcVolInfo.getDataStore().getId());
 +        final DevelopersApi api = 
LinstorUtil.getLinstorAPI(pool.getHostAddress());
 +        final String rscName = LinstorUtil.RSC_PREFIX + srcVolInfo.getUuid();
 +
 +        VolumeObjectTO to = (VolumeObjectTO) srcVolInfo.getTO();
 +        // patch source format
 +        // Linstor volumes are stored as RAW, but we can't set the correct 
format as RAW (we use QCOW2)
 +        // otherwise create template from snapshot won't work, because this 
operation
 +        // uses the format of the base volume and we backup snapshots as QCOW2
 +        // 
https://github.com/apache/cloudstack/pull/8802#issuecomment-2024019927
 +        to.setFormat(Storage.ImageFormat.RAW);
 +        int nMaxExecutionSeconds = NumbersUtil.parseInt(
 +                _configDao.getValue(Config.CopyVolumeWait.key()), 10800);
 +        CopyCommand cmd = new CopyCommand(
 +                to,
 +                dstData.getTO(),
 +                nMaxExecutionSeconds,
 +                VirtualMachineManager.ExecuteInSequence.value());
 +        Answer answer;
 +
 +        try {
 +            Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, rscName);
 +            if (optEP.isPresent()) {
 +                answer = optEP.get().sendMessage(cmd);
 +            }
 +            else {
 +                answer = new Answer(cmd, false, "Unable to get matching 
Linstor endpoint.");
 +            }
 +        } catch (ApiException exc) {
 +            s_logger.error("copy volume failed: ", exc);
 +            throw new CloudRuntimeException(exc.getBestMessage());
 +        }
 +        return answer;
 +    }
 +
 +    /**
 +     * Create a temporary resource from the snapshot to backup, so we can 
copy the data on a diskless agent
 +     * @param api Linstor Developer api object
 +     * @param pool StoragePool this resource resides on
 +     * @param rscName rscName of the snapshotted resource
 +     * @param snapshotInfo snapshot info of the snapshot
 +     * @param origCmd original LinstorBackupSnapshotCommand that needs to 
have a patched path
 +     * @return answer from agent operation
 +     * @throws ApiException if any Linstor api operation fails
 +     */
 +    private Answer copyFromTemporaryResource(
 +            DevelopersApi api, StoragePoolVO pool, String rscName, 
SnapshotInfo snapshotInfo, CopyCommand origCmd)
 +            throws ApiException {
 +        Answer answer;
 +        String restoreName = rscName + "-rst";
 +        String snapshotName = LinstorUtil.RSC_PREFIX + snapshotInfo.getUuid();
 +        String devName = restoreResourceFromSnapshot(api, pool, rscName, 
snapshotName, restoreName);
 +
 +        Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, 
restoreName);
 +        if (optEPAny.isPresent()) {
 +            // patch the src device path to the temporary linstor resource
 +            SnapshotObjectTO soTO = (SnapshotObjectTO)snapshotInfo.getTO();
 +            soTO.setPath(devName);
 +            origCmd.setSrcTO(soTO);
 +            answer = optEPAny.get().sendMessage(origCmd);
 +        } else{
 +            answer = new Answer(origCmd, false, "Unable to get matching 
Linstor endpoint.");
 +        }
 +        // delete the temporary resource, noop if already gone
 +        api.resourceDefinitionDelete(restoreName);
 +        return answer;
 +    }
 +
 +    protected Answer copySnapshot(DataObject srcData, DataObject destData) {
 +        String value = 
_configDao.getValue(Config.BackupSnapshotWait.toString());
 +        int _backupsnapshotwait = NumbersUtil.parseInt(
 +            value, 
Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue()));
 +
 +        SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
 +        Boolean snapshotFullBackup = snapshotInfo.getFullBackup();
 +        final StoragePoolVO pool = 
_storagePoolDao.findById(srcData.getDataStore().getId());
 +        final DevelopersApi api = 
LinstorUtil.getLinstorAPI(pool.getHostAddress());
 +        boolean fullSnapshot = true;
 +        if (snapshotFullBackup != null) {
 +            fullSnapshot = snapshotFullBackup;
 +        }
 +        Map<String, String> options = new HashMap<>();
 +        options.put("fullSnapshot", fullSnapshot + "");
 +        options.put(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(),
 +            
String.valueOf(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value()));
 +        options.put("volumeSize", snapshotInfo.getBaseVolume().getSize() + 
"");
 +
 +        try {
 +            CopyCommand cmd = new LinstorBackupSnapshotCommand(
 +                srcData.getTO(),
 +                destData.getTO(),
 +                _backupsnapshotwait,
 +                VirtualMachineManager.ExecuteInSequence.value());
 +            cmd.setOptions(options);
 +
 +            String rscName = LinstorUtil.RSC_PREFIX + 
snapshotInfo.getBaseVolume().getUuid();
 +            Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, rscName);
 +            Answer answer;
 +            if (optEP.isPresent()) {
 +                answer = optEP.get().sendMessage(cmd);
 +            } else {
 +                s_logger.debug("No diskfull endpoint found to copy image, 
creating diskless endpoint");
 +                answer = copyFromTemporaryResource(api, pool, rscName, 
snapshotInfo, cmd);
 +            }
 +            return answer;
 +        } catch (Exception e) {
 +            s_logger.debug("copy snapshot failed: ", e);
 +            throw new CloudRuntimeException(e.toString());
 +        }
 +
      }
  
      @Override

Reply via email to