diff --git a/conf/serviceConfig/primaryStorage.xml b/conf/serviceConfig/primaryStorage.xml index 337ce4eaac3..c7b2a7d558a 100755 --- a/conf/serviceConfig/primaryStorage.xml +++ b/conf/serviceConfig/primaryStorage.xml @@ -81,7 +81,16 @@ org.zstack.header.storage.primary.APICleanUpStorageTrashOnPrimaryStorageMsg + org.zstack.header.storage.primary.APIAddStorageProtocolMsg + + + org.zstack.header.storage.primary.APITakeoverPrimaryStorageMsg + + + + org.zstack.header.storage.primary.APIDiscoverStrangePrimaryStorageMsg + diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageMsg.java new file mode 100644 index 00000000000..f1d4e5d0c6b --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageMsg.java @@ -0,0 +1,31 @@ +package org.zstack.header.storage.primary; + +import org.springframework.http.HttpMethod; +import org.zstack.header.cluster.ClusterVO; +import org.zstack.header.message.APIParam; +import org.zstack.header.message.APISyncCallMessage; +import org.zstack.header.rest.RestRequest; + +@RestRequest( + path = "/primary-storage/stranger", + method = HttpMethod.GET, + responseClass = APIDiscoverStrangePrimaryStorageReply.class +) +public class APIDiscoverStrangePrimaryStorageMsg extends APISyncCallMessage { + @APIParam(resourceType = ClusterVO.class) + private String clusterUuid; + + public String getClusterUuid() { + return clusterUuid; + } + + public void setClusterUuid(String clusterUuid) { + this.clusterUuid = clusterUuid; + } + + public static APIDiscoverStrangePrimaryStorageMsg __example__() { + APIDiscoverStrangePrimaryStorageMsg msg = new APIDiscoverStrangePrimaryStorageMsg(); + msg.setClusterUuid(uuid()); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..043467cd721 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageMsgDoc_zh_cn.groovy @@ -0,0 +1,58 @@ +package org.zstack.header.storage.primary + +import org.zstack.header.storage.primary.APIDiscoverStrangePrimaryStorageReply + +doc { + title "DiscoverStrangePrimaryStorage" + + category "storage.primary" + + desc """发现集群物理机上未受当前平台管控的主存储""" + + rest { + request { + url "GET /v1/primary-storage/stranger" + + header (Authorization: 'OAuth the-session-uuid') + + clz APIDiscoverStrangePrimaryStorageMsg.class + + desc """""" + + params { + + column { + name "clusterUuid" + enclosedIn "" + desc "集群UUID" + location "query" + type "String" + optional false + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "query" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "query" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APIDiscoverStrangePrimaryStorageReply.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageReply.java b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageReply.java new file mode 100644 index 00000000000..704ee1f5de2 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageReply.java @@ -0,0 +1,34 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.APIReply; +import org.zstack.header.rest.RestResponse; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@RestResponse(fieldsTo = {"all"}) +public class APIDiscoverStrangePrimaryStorageReply extends APIReply { + private List inventories = new ArrayList<>(); + + public List getInventories() { + return inventories; + } + + public void setInventories(List inventories) { + this.inventories = inventories; + } + + public static APIDiscoverStrangePrimaryStorageReply __example__() { + APIDiscoverStrangePrimaryStorageReply reply = new APIDiscoverStrangePrimaryStorageReply(); + + PrimaryStorageInventory ps = new PrimaryStorageInventory(); + ps.setName("SharedBlockGroup-1"); + ps.setUrl("/dev/vg_uuid"); + ps.setType("SharedBlock"); + ps.setAttachedClusterUuids(Collections.singletonList(uuid())); + reply.setInventories(Collections.singletonList(ps)); + + return reply; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageReplyDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageReplyDoc_zh_cn.groovy new file mode 100644 index 00000000000..8c39eaa987c --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APIDiscoverStrangePrimaryStorageReplyDoc_zh_cn.groovy @@ -0,0 +1,29 @@ +package org.zstack.header.storage.primary + +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "发现集群物理机上未受当前平台管控的主存储返回" + + field { + name "inventories" + desc "发现的主存储清单列表" + type "List" + since "5.0.0" + } + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.storage.primary.APIDiscoverStrangePrimaryStorageReply.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageEvent.java b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageEvent.java new file mode 100644 index 00000000000..81e7bc13a0a --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageEvent.java @@ -0,0 +1,39 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.message.APIEvent; +import org.zstack.header.rest.RestResponse; + +import java.util.Collections; + +@RestResponse(fieldsTo = {"all"}) +public class APITakeoverPrimaryStorageEvent extends APIEvent { + private PrimaryStorageInventory inventory; + + public APITakeoverPrimaryStorageEvent() { + } + + public APITakeoverPrimaryStorageEvent(String apiId) { + super(apiId); + } + + public PrimaryStorageInventory getInventory() { + return inventory; + } + + public void setInventory(PrimaryStorageInventory inventory) { + this.inventory = inventory; + } + + public static APITakeoverPrimaryStorageEvent __example__() { + APITakeoverPrimaryStorageEvent event = new APITakeoverPrimaryStorageEvent(); + + PrimaryStorageInventory ps = new PrimaryStorageInventory(); + ps.setName("PS1"); + ps.setUrl("/zstack_ps"); + ps.setType("SharedBlock"); + ps.setAttachedClusterUuids(Collections.singletonList(uuid())); + + event.setInventory(ps); + return event; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageEventDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageEventDoc_zh_cn.groovy new file mode 100644 index 00000000000..ad6515183af --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageEventDoc_zh_cn.groovy @@ -0,0 +1,32 @@ +package org.zstack.header.storage.primary + +import org.zstack.header.storage.primary.PrimaryStorageInventory +import org.zstack.header.errorcode.ErrorCode + +doc { + + title "接管主存储返回" + + ref { + name "inventory" + path "org.zstack.header.storage.primary.APITakeoverPrimaryStorageEvent.inventory" + desc "主存储信息" + type "PrimaryStorageInventory" + since "5.0.0" + clz PrimaryStorageInventory.class + } + field { + name "success" + desc "操作是否成功" + type "boolean" + since "5.0.0" + } + ref { + name "error" + path "org.zstack.header.storage.primary.APITakeoverPrimaryStorageEvent.error" + desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null" + type "ErrorCode" + since "5.0.0" + clz ErrorCode.class + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageMsg.java b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageMsg.java new file mode 100644 index 00000000000..5f5cc4e3345 --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageMsg.java @@ -0,0 +1,52 @@ +package org.zstack.header.storage.primary; + +import org.springframework.http.HttpMethod; +import org.zstack.header.message.APIMessage; +import org.zstack.header.message.APIParam; +import org.zstack.header.message.DefaultTimeout; +import org.zstack.header.rest.RestRequest; + +import java.util.concurrent.TimeUnit; + +@RestRequest( + path = "/primary-storage/{uuid}/takeover", + responseClass = APITakeoverPrimaryStorageEvent.class, + method = HttpMethod.PUT, + isAction = true +) +@DefaultTimeout(timeunit = TimeUnit.HOURS, value = 1) +public class APITakeoverPrimaryStorageMsg extends APIMessage implements PrimaryStorageMessage { + @APIParam(resourceType = PrimaryStorageVO.class) + private String uuid; + + @APIParam(required = false) + private boolean dryRun; + + @Override + public String getPrimaryStorageUuid() { + return uuid; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public boolean isDryRun() { + return dryRun; + } + + public void setDryRun(boolean dryRun) { + this.dryRun = dryRun; + } + + public static APITakeoverPrimaryStorageMsg __example__() { + APITakeoverPrimaryStorageMsg msg = new APITakeoverPrimaryStorageMsg(); + msg.setUuid(uuid(PrimaryStorageVO.class)); + msg.setDryRun(false); + return msg; + } +} diff --git a/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageMsgDoc_zh_cn.groovy b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageMsgDoc_zh_cn.groovy new file mode 100644 index 00000000000..7f46140e45f --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/APITakeoverPrimaryStorageMsgDoc_zh_cn.groovy @@ -0,0 +1,70 @@ +package org.zstack.header.storage.primary + +import org.zstack.header.storage.primary.APITakeoverPrimaryStorageEvent + +doc { + title "TakeoverPrimaryStorage" + + category "storage.primary" + + desc """接管主存储。将其他ZStack平台的共享块主存储接管到当前平台。 +dryRun=true时仅执行一致性预检:若一致(无需接管)则返回错误,若不一致(可接管)则返回成功。 +接管操作不可逆(agent侧会执行VG rename、PV UUID reset、sanlock lockspace reset)。 +接管完成后自动触发reconnect,通过返回的inventory.status获取重连状态。""" + + rest { + request { + url "PUT /v1/primary-storage/{uuid}/takeover" + + header (Authorization: 'OAuth the-session-uuid') + + clz APITakeoverPrimaryStorageMsg.class + + desc """接管指定主存储""" + + params { + + column { + name "uuid" + enclosedIn "takeoverPrimaryStorage" + desc "主存储的UUID" + location "url" + type "String" + optional false + since "5.0.0" + } + column { + name "dryRun" + enclosedIn "takeoverPrimaryStorage" + desc "预检模式:成功表示可以执行接管,失败表示无需或无法接管" + location "body" + type "boolean" + optional true + since "5.0.0" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "body" + type "List" + optional true + since "5.0.0" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "body" + type "List" + optional true + since "5.0.0" + } + } + } + + response { + clz APITakeoverPrimaryStorageEvent.class + } + } +} \ No newline at end of file diff --git a/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageDiscoverExtensionPoint.java b/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageDiscoverExtensionPoint.java new file mode 100644 index 00000000000..2790c047ece --- /dev/null +++ b/header/src/main/java/org/zstack/header/storage/primary/PrimaryStorageDiscoverExtensionPoint.java @@ -0,0 +1,14 @@ +package org.zstack.header.storage.primary; + +import org.zstack.header.core.ReturnValueCompletion; + +import java.util.List; + +/** + * Extension point for discovering unmanaged (strange) primary storage. + * Each primary storage type can implement this to discover its own PS instances + * that exist on hosts but are not managed by the platform. + */ +public interface PrimaryStorageDiscoverExtensionPoint { + void discoverStrangePrimaryStorage(String clusterUuid, ReturnValueCompletion> completion); +} diff --git a/sdk/src/main/java/org/zstack/sdk/DiscoverStrangePrimaryStorageAction.java b/sdk/src/main/java/org/zstack/sdk/DiscoverStrangePrimaryStorageAction.java new file mode 100644 index 00000000000..3adabc73102 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/DiscoverStrangePrimaryStorageAction.java @@ -0,0 +1,95 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class DiscoverStrangePrimaryStorageAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.DiscoverStrangePrimaryStorageResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String clusterUuid; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.DiscoverStrangePrimaryStorageResult value = res.getResult(org.zstack.sdk.DiscoverStrangePrimaryStorageResult.class); + ret.value = value == null ? new org.zstack.sdk.DiscoverStrangePrimaryStorageResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "GET"; + info.path = "/primary-storage/stranger"; + info.needSession = true; + info.needPoll = false; + info.parameterName = ""; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/DiscoverStrangePrimaryStorageResult.java b/sdk/src/main/java/org/zstack/sdk/DiscoverStrangePrimaryStorageResult.java new file mode 100644 index 00000000000..714607ff063 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/DiscoverStrangePrimaryStorageResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk; + + + +public class DiscoverStrangePrimaryStorageResult { + public java.util.List inventories; + public void setInventories(java.util.List inventories) { + this.inventories = inventories; + } + public java.util.List getInventories() { + return this.inventories; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/SharedBlockInventory.java b/sdk/src/main/java/org/zstack/sdk/SharedBlockInventory.java index 2f7e294b9af..eac9ff43c44 100644 --- a/sdk/src/main/java/org/zstack/sdk/SharedBlockInventory.java +++ b/sdk/src/main/java/org/zstack/sdk/SharedBlockInventory.java @@ -102,4 +102,12 @@ public java.lang.Long getAvailableCapacity() { return this.availableCapacity; } + public java.lang.String vendor; + public void setVendor(java.lang.String vendor) { + this.vendor = vendor; + } + public java.lang.String getVendor() { + return this.vendor; + } + } diff --git a/sdk/src/main/java/org/zstack/sdk/TakeoverPrimaryStorageAction.java b/sdk/src/main/java/org/zstack/sdk/TakeoverPrimaryStorageAction.java new file mode 100644 index 00000000000..0be12e75a7a --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/TakeoverPrimaryStorageAction.java @@ -0,0 +1,104 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class TakeoverPrimaryStorageAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.TakeoverPrimaryStorageResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s]", error.code, error.description, error.details) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String uuid; + + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public boolean dryRun = false; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.TakeoverPrimaryStorageResult value = res.getResult(org.zstack.sdk.TakeoverPrimaryStorageResult.class); + ret.value = value == null ? new org.zstack.sdk.TakeoverPrimaryStorageResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "PUT"; + info.path = "/primary-storage/{uuid}/takeover"; + info.needSession = true; + info.needPoll = true; + info.parameterName = "takeoverPrimaryStorage"; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/TakeoverPrimaryStorageResult.java b/sdk/src/main/java/org/zstack/sdk/TakeoverPrimaryStorageResult.java new file mode 100644 index 00000000000..f6fc1d6b138 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/TakeoverPrimaryStorageResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk; + +import org.zstack.sdk.PrimaryStorageInventory; + +public class TakeoverPrimaryStorageResult { + public PrimaryStorageInventory inventory; + public void setInventory(PrimaryStorageInventory inventory) { + this.inventory = inventory; + } + public PrimaryStorageInventory getInventory() { + return this.inventory; + } + +} diff --git a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java index b7f8cfbc24d..87e1f2c821d 100755 --- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java +++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageBase.java @@ -935,11 +935,19 @@ protected void handleApiMessage(APIMessage msg) { handle((APICleanUpStorageTrashOnPrimaryStorageMsg) msg); } else if (msg instanceof APIAddStorageProtocolMsg) { handle((APIAddStorageProtocolMsg) msg); + } else if (msg instanceof APITakeoverPrimaryStorageMsg) { + handle((APITakeoverPrimaryStorageMsg) msg); } else { bus.dealWithUnknownMessage(msg); } } + protected void handle(APITakeoverPrimaryStorageMsg msg) { + APITakeoverPrimaryStorageEvent event = new APITakeoverPrimaryStorageEvent(msg.getId()); + event.setError(operr("takeover not supported for primary storage type[%s]", self.getType())); + bus.publish(event); + } + private void handle(APIAddStorageProtocolMsg msg) { APIAddStorageProtocolEvent evt = new APIAddStorageProtocolEvent(msg.getId()); addStorageProtocol(msg, new Completion(msg) { diff --git a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageManagerImpl.java b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageManagerImpl.java index a84ed42f7be..a1139d84db2 100755 --- a/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageManagerImpl.java +++ b/storage/src/main/java/org/zstack/storage/primary/PrimaryStorageManagerImpl.java @@ -9,6 +9,7 @@ import org.zstack.configuration.InstanceOfferingSystemTags; import org.zstack.configuration.OfferingUserConfigUtils; import org.zstack.core.Platform; +import org.zstack.core.asyncbatch.While; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.cloudbus.MessageSafe; @@ -35,9 +36,12 @@ import org.zstack.header.configuration.userconfig.InstanceOfferingUserConfig; import org.zstack.header.configuration.userconfig.InstanceOfferingUserConfigValidator; import org.zstack.header.core.NoErrorCompletion; +import org.zstack.header.core.ReturnValueCompletion; +import org.zstack.header.core.WhileDoneCompletion; import org.zstack.header.core.trash.InstallPathRecycleVO; import org.zstack.header.core.trash.InstallPathRecycleVO_; import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.errorcode.ErrorCodeList; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.errorcode.SysErrors; import org.zstack.header.exception.CloudRuntimeException; @@ -149,6 +153,8 @@ private void handleApiMessage(APIMessage msg) { handle((APIGetPrimaryStorageLicenseInfoMsg) msg); } else if (msg instanceof APIGetPrimaryStorageUsageReportMsg) { handle((APIGetPrimaryStorageUsageReportMsg) msg); + } else if (msg instanceof APIDiscoverStrangePrimaryStorageMsg) { + handle((APIDiscoverStrangePrimaryStorageMsg) msg); } else { bus.dealWithUnknownMessage(msg); } @@ -290,6 +296,42 @@ public void run(MessageReply r) { }); } + private void handle(APIDiscoverStrangePrimaryStorageMsg msg) { + APIDiscoverStrangePrimaryStorageReply reply = new APIDiscoverStrangePrimaryStorageReply(); + + List exts = pluginRgty.getExtensionList(PrimaryStorageDiscoverExtensionPoint.class); + if (exts.isEmpty()) { + bus.reply(msg, reply); + return; + } + + new While<>(exts).all((ext, whileCompletion) -> { + ext.discoverStrangePrimaryStorage(msg.getClusterUuid(), new ReturnValueCompletion>(whileCompletion) { + @Override + public void success(List inventories) { + if (inventories != null) { + synchronized (reply) { + reply.getInventories().addAll(inventories); + } + } + whileCompletion.done(); + } + + @Override + public void fail(ErrorCode errorCode) { + logger.warn(String.format("failed to discover strange primary storage from extension[%s]: %s", + ext.getClass().getSimpleName(), errorCode)); + whileCompletion.done(); + } + }); + }).run(new WhileDoneCompletion(msg) { + @Override + public void done(ErrorCodeList errorCodeList) { + bus.reply(msg, reply); + } + }); + } + private void passThrough(PrimaryStorageMessage pmsg) { PrimaryStorageVO vo = dbf.findByUuid(pmsg.getPrimaryStorageUuid(), PrimaryStorageVO.class); if (vo == null && allowedMessageAfterSoftDeletion.contains(pmsg.getClass())) { diff --git a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy index 515c1f6ce99..8a7b1396de6 100644 --- a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy +++ b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy @@ -13490,6 +13490,33 @@ abstract class ApiHelper { } + def discoverStrangePrimaryStorage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.DiscoverStrangePrimaryStorageAction.class) Closure c) { + def a = new org.zstack.sdk.DiscoverStrangePrimaryStorageAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def enableCbtTask(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.EnableCbtTaskAction.class) Closure c) { def a = new org.zstack.sdk.EnableCbtTaskAction() a.sessionId = Test.currentEnvSpec?.session?.uuid @@ -30484,6 +30511,33 @@ abstract class ApiHelper { } + def takeoverPrimaryStorage(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.TakeoverPrimaryStorageAction.class) Closure c) { + def a = new org.zstack.sdk.TakeoverPrimaryStorageAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def triggerGCJob(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.TriggerGCJobAction.class) Closure c) { def a = new org.zstack.sdk.TriggerGCJobAction() a.sessionId = Test.currentEnvSpec?.session?.uuid