diff --git a/controller/src/main/java/com/linbit/linstor/api/rest/v1/PhysicalStorage.java b/controller/src/main/java/com/linbit/linstor/api/rest/v1/PhysicalStorage.java index d6b9a452d..b9458aab8 100644 --- a/controller/src/main/java/com/linbit/linstor/api/rest/v1/PhysicalStorage.java +++ b/controller/src/main/java/com/linbit/linstor/api/rest/v1/PhysicalStorage.java @@ -6,6 +6,9 @@ import com.linbit.linstor.api.rest.v1.serializer.JsonGenTypes; import com.linbit.linstor.api.rest.v1.utils.ApiCallRcRestUtils; import com.linbit.linstor.core.apicallhandler.controller.CtrlPhysicalStorageApiCallHandler; +import com.linbit.linstor.core.apicallhandler.controller.CtrlStorPoolCrtApiCallHandler; +import com.linbit.linstor.storage.LvmThinDriverKind; +import com.linbit.linstor.storage.kinds.DeviceProviderKind; import javax.inject.Inject; import javax.ws.rs.Consumes; @@ -21,9 +24,10 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; - import java.io.IOException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -39,16 +43,19 @@ public class PhysicalStorage { private final RequestHelper requestHelper; private final CtrlPhysicalStorageApiCallHandler physicalStorageApiCallHandler; + private final CtrlStorPoolCrtApiCallHandler storPoolCrtApiCallHandler; private final ObjectMapper objectMapper; @Inject public PhysicalStorage( RequestHelper requestHelperRef, - CtrlPhysicalStorageApiCallHandler ctrlPhysicalStorageApiCallHandler + CtrlPhysicalStorageApiCallHandler ctrlPhysicalStorageApiCallHandler, + CtrlStorPoolCrtApiCallHandler ctrlStorPoolCrtApiCallHandler ) { requestHelper = requestHelperRef; physicalStorageApiCallHandler = ctrlPhysicalStorageApiCallHandler; + storPoolCrtApiCallHandler = ctrlStorPoolCrtApiCallHandler; objectMapper = new ObjectMapper(); } @@ -95,6 +102,33 @@ public void listPhysicalStorage( }); } + private Map deviceProviderToStorPoolProperty(DeviceProviderKind kind, String pool) + { + HashMap map = new HashMap<>(); + + switch (kind) + { + case LVM: + map.put(ApiConsts.NAMESPC_STORAGE_DRIVER + "/" + ApiConsts.KEY_STOR_POOL_VOLUME_GROUP, pool); + break; + case LVM_THIN: + map.put( + ApiConsts.NAMESPC_STORAGE_DRIVER + "/" + ApiConsts.KEY_STOR_POOL_VOLUME_GROUP, + LvmThinDriverKind.VG_PREFIX + pool + ); + map.put(ApiConsts.NAMESPC_STORAGE_DRIVER + "/" + ApiConsts.KEY_STOR_POOL_THIN_POOL, pool); + break; + case ZFS: + map.put(ApiConsts.NAMESPC_STORAGE_DRIVER + "/" + ApiConsts.KEY_STOR_POOL_ZPOOL, pool); + break; + default: + //ignore + break; + } + + return map; + } + @POST @Consumes(MediaType.APPLICATION_JSON) @Path("{nodeName}") @@ -110,17 +144,50 @@ public void createDevicePool( JsonGenTypes.PhysicalStorageCreate createData = objectMapper .readValue(jsonData, JsonGenTypes.PhysicalStorageCreate.class); + // check if with_storage_pool is given and if storage pool name is valid, before we do unneeded create/del + if (createData.with_storage_pool != null) + { + LinstorParsingUtils.asStorPoolName(createData.with_storage_pool.name); + } + + final String poolName = CtrlPhysicalStorageApiCallHandler.getDevicePoolName( + createData.pool_name, + createData.device_paths + ); + + final DeviceProviderKind deviceProviderKind = LinstorParsingUtils.asProviderKind(createData.provider_kind); Flux responses = physicalStorageApiCallHandler.createDevicePool( nodeName, createData.device_paths, - LinstorParsingUtils.asProviderKind(createData.provider_kind), + deviceProviderKind, LinstorParsingUtils.asRaidLevel(createData.raid_level), - createData.pool_name, + poolName, createData.vdo_enable, createData.vdo_logical_size_kib, createData.vdo_slab_size_kib ).subscriberContext(requestHelper.createContext(ApiConsts.API_CREATE_DEVICE_POOL, request)); + if (createData.with_storage_pool != null) + { + Map storPoolProps = deviceProviderToStorPoolProperty(deviceProviderKind, poolName); + storPoolProps.putAll(createData.with_storage_pool.props); + responses = responses.concatWith( + storPoolCrtApiCallHandler.createStorPool( + nodeName, + createData.with_storage_pool.name, + deviceProviderKind, + null, + storPoolProps, + physicalStorageApiCallHandler.deleteDevicePool( + nodeName, + createData.device_paths, + deviceProviderKind, + poolName + ) + ).subscriberContext(requestHelper.createContext(ApiConsts.API_CRT_STOR_POOL, request)) + ); + } + requestHelper.doFlux( asyncResponse, ApiCallRcRestUtils.mapToMonoResponse(responses, Response.Status.CREATED) diff --git a/controller/src/main/java/com/linbit/linstor/api/rest/v1/serializer/JsonGenTypes.java b/controller/src/main/java/com/linbit/linstor/api/rest/v1/serializer/JsonGenTypes.java index 4584c74bc..2100f1c05 100644 --- a/controller/src/main/java/com/linbit/linstor/api/rest/v1/serializer/JsonGenTypes.java +++ b/controller/src/main/java/com/linbit/linstor/api/rest/v1/serializer/JsonGenTypes.java @@ -12,7 +12,7 @@ public class JsonGenTypes { - public static final String REST_API_VERSION = "1.0.13"; + public static final String REST_API_VERSION = "1.0.12"; /** * Common api reply structure @@ -821,6 +821,19 @@ public static class PhysicalStorageDevice public String wwn; } + /** + * This structured is used for create physical-storage + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public static class PhysicalStorageStoragePoolCreate + { + /** + * Name of the linstor storage pool + */ + public String name; + public Map props = Collections.emptyMap(); + } + @JsonInclude(JsonInclude.Include.NON_EMPTY) public static class PhysicalStorageCreate { @@ -834,6 +847,10 @@ public static class PhysicalStorageCreate public boolean vdo_enable = false; public int vdo_slab_size_kib = 0; public int vdo_logical_size_kib = 0; + /** + * If specified a linstor storage pool will also be created using this device pool + */ + public PhysicalStorageStoragePoolCreate with_storage_pool; } private JsonGenTypes() diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlPhysicalStorageApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlPhysicalStorageApiCallHandler.java index 54d5b310e..05ad900a6 100644 --- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlPhysicalStorageApiCallHandler.java +++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlPhysicalStorageApiCallHandler.java @@ -6,7 +6,6 @@ import com.linbit.linstor.api.ApiCallRc; import com.linbit.linstor.api.ApiConsts; import com.linbit.linstor.api.interfaces.serializer.CtrlStltSerializer; -import com.linbit.linstor.api.rest.v1.serializer.Json; import com.linbit.linstor.api.rest.v1.serializer.JsonGenTypes; import com.linbit.linstor.core.apicallhandler.ScopeRunner; import com.linbit.linstor.core.apicallhandler.controller.internal.CtrlSatelliteUpdateCaller; @@ -151,6 +150,20 @@ private static List parsePhysicalDevices(ByteArrayInputStream inputS } } + public static String getDevicePoolName(final String devicePoolName, final List devicePaths) + { + String poolName = devicePoolName; + if (devicePoolName == null || devicePoolName.isEmpty()) + { + final String devicePath = devicePaths != null && !devicePaths.isEmpty() ? devicePaths.get(0) : "unknown"; + // create pool name + final int lastSlash = devicePath.lastIndexOf('/'); + poolName = "linstor_" + (lastSlash > 0 ? devicePath.substring(lastSlash + 1) : devicePath); + } + + return poolName; + } + public Flux createDevicePool( String nodeNameStr, List devicePaths, @@ -202,14 +215,7 @@ private Flux createDevicePoolInScope( throw new ApiException("Field 'device_paths' is null or empty."); } - String poolName = poolNameArg; - if (poolNameArg == null || poolNameArg.isEmpty()) - { - final String devicePath = devicePaths.get(0); - // create pool name - final int lastSlash = devicePath.lastIndexOf('/'); - poolName = "linstor_" + (lastSlash > 0 ? devicePath.substring(lastSlash + 1) : devicePath); - } + String poolName = getDevicePoolName(poolNameArg, devicePaths); try { @@ -242,6 +248,62 @@ private Flux createDevicePoolInScope( return response; } + public Flux deleteDevicePool( + String nodeNameStr, + List devicePaths, + DeviceProviderKind providerKindRef, + String poolName + ) + { + return scopeRunner.fluxInTransactionlessScope( + "DeleteDevicePool", + lockGuardFactory.buildDeferred(LockGuardFactory.LockType.READ, LockGuardFactory.LockObj.NODES_MAP), + () -> deleteDevicePoolInScope( + nodeNameStr, + devicePaths, + providerKindRef, + poolName + ) + ); + } + + private Flux deleteDevicePoolInScope( + String nodeNameStr, + List devicePaths, + DeviceProviderKind providerKindRef, + String poolName + ) + { + Flux response; + Node node = ctrlApiDataLoader.loadNode(nodeNameStr, true); + + try + { + response = node.getPeer(peerAccCtx.get()) + .apiCall( + InternalApiConsts.API_DELETE_DEVICE_POOL, + ctrlStltSerializer.headerlessBuilder() + .deleteDevicePool( + devicePaths, + providerKindRef, + poolName + ).build() + ) + .onErrorResume(PeerNotConnectedException.class, ignored -> Flux.empty()) + .map(answer -> CtrlSatelliteUpdateCaller.deserializeApiCallRc(node.getName(), answer)); + } + catch (AccessDeniedException accExc) + { + throw new ApiAccessDeniedException( + accExc, + "get peer from node", + ApiConsts.FAIL_ACC_DENIED_NODE + ); + } + + return response; + } + public static List groupPhysicalStorageByDevice( Map> mapData ) diff --git a/docs/rest_v1_openapi.yaml b/docs/rest_v1_openapi.yaml index 978c22649..8899c6c75 100644 --- a/docs/rest_v1_openapi.yaml +++ b/docs/rest_v1_openapi.yaml @@ -21,10 +21,10 @@ info: Changelog: - * 1.0.13 - - Removed support for swordfish * 1.0.12 - Added WritecacheResource and WritecacheVolume schemas. + - Removed support for swordfish + - Added `with_storage_pool` to PhysicalStorageCreate post request, allowing to create linstor storage pools too * 1.0.11 - Added /v1/physical-storage endpoint, that lets you query and create lvm/zfs pools - Extended Node with list of supported providers and layers as well as lists of reasons for @@ -63,7 +63,7 @@ info: - no functional changes * 1.0.0 - Initial REST API v1 - version: 1.0.13 + version: 1.0.12 title: Linstor REST API contact: email: rene.peinthor@linbit.com @@ -4510,6 +4510,15 @@ components: type: string wwn: type: string + PhysicalStorageStoragePoolCreate: + type: object + description: This structured is used for create physical-storage + properties: + name: + type: string + description: Name of the linstor storage pool + props: + $ref: '#/components/schemas/Properties' PhysicalStorageCreate: type: object required: @@ -4542,3 +4551,6 @@ components: type: integer format: uint64 default: 0 + with_storage_pool: + description: If specified a linstor storage pool will also be created using this device pool + $ref: '#/components/schemas/PhysicalStorageStoragePoolCreate' diff --git a/linstor-common b/linstor-common index 5f9c78f66..3da619554 160000 --- a/linstor-common +++ b/linstor-common @@ -1 +1 @@ -Subproject commit 5f9c78f6613b39cede6281dd7c8551f295c0602d +Subproject commit 3da619554919be85b542f77370bd62c4f1e34ecc diff --git a/satellite/src/main/java/com/linbit/linstor/api/protobuf/satellite/DeleteDevicePool.java b/satellite/src/main/java/com/linbit/linstor/api/protobuf/satellite/DeleteDevicePool.java new file mode 100644 index 000000000..6e50a38d1 --- /dev/null +++ b/satellite/src/main/java/com/linbit/linstor/api/protobuf/satellite/DeleteDevicePool.java @@ -0,0 +1,74 @@ +package com.linbit.linstor.api.protobuf.satellite; + +import com.linbit.linstor.InternalApiConsts; +import com.linbit.linstor.api.ApiCall; +import com.linbit.linstor.api.ApiCallRcImpl; +import com.linbit.linstor.api.ApiConsts; +import com.linbit.linstor.api.ApiModule; +import com.linbit.linstor.api.interfaces.serializer.CtrlStltSerializer; +import com.linbit.linstor.api.protobuf.ProtoDeserializationUtils; +import com.linbit.linstor.api.protobuf.ProtobufApiCall; +import com.linbit.linstor.netcom.Peer; +import com.linbit.linstor.proto.javainternal.c2s.MsgDeleteDevicePoolOuterClass.MsgDeleteDevicePool; +import com.linbit.linstor.storage.DevicePoolHandler; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@ProtobufApiCall( + name = InternalApiConsts.API_DELETE_DEVICE_POOL, + description = "Deletes a device pool" +) +@Singleton +public class DeleteDevicePool implements ApiCall +{ + private final Provider peerProvider; + private Provider apiCallId; + private final CtrlStltSerializer ctrlStltSerializer; + private final DevicePoolHandler devicePoolHandler; + + @Inject + public DeleteDevicePool( + Provider peerProviderRef, + @Named(ApiModule.API_CALL_ID) Provider apiCallIdRef, + CtrlStltSerializer ctrlStltSerializerRef, + DevicePoolHandler devicePoolHandlerRef + ) + { + this.peerProvider = peerProviderRef; + this.apiCallId = apiCallIdRef; + this.ctrlStltSerializer = ctrlStltSerializerRef; + this.devicePoolHandler = devicePoolHandlerRef; + } + + @Override + public void execute(InputStream msgDataIn) throws IOException + { + MsgDeleteDevicePool msgDeleteDevicePool = + MsgDeleteDevicePool.parseDelimitedFrom(msgDataIn); + + ApiCallRcImpl apiCallRc = new ApiCallRcImpl(); + + List devicePaths = msgDeleteDevicePool.getDevicePathsList(); + ArrayList lvmDevicePaths = new ArrayList<>(devicePaths); + + if (!lvmDevicePaths.isEmpty()) + { + apiCallRc.addEntries(devicePoolHandler.deleteDevicePool( + ProtoDeserializationUtils.parseDeviceProviderKind(msgDeleteDevicePool.getProviderKind()), + lvmDevicePaths, + msgDeleteDevicePool.getPoolName() + )); + } + + peerProvider.get().sendMessage( + ctrlStltSerializer.answerBuilder(ApiConsts.API_REPLY, apiCallId.get()).apiCallRcSeries(apiCallRc).build() + ); + } +} diff --git a/satellite/src/main/java/com/linbit/linstor/storage/DevicePoolHandler.java b/satellite/src/main/java/com/linbit/linstor/storage/DevicePoolHandler.java index d993ffd49..14a731e38 100644 --- a/satellite/src/main/java/com/linbit/linstor/storage/DevicePoolHandler.java +++ b/satellite/src/main/java/com/linbit/linstor/storage/DevicePoolHandler.java @@ -20,8 +20,6 @@ @Singleton public class DevicePoolHandler { - private static final String VG_PREFIX = "linstor_"; - private final ErrorReporter errorReporter; private final ExtCmdFactory extCmdFactory; @@ -69,7 +67,7 @@ public String createVdoDevice( ); apiCallRc.addEntry(ApiCallRcImpl.simpleEntry( - ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT, + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT | ApiConsts.MASK_PHYSICAL_DEVICE, String.format("VDO '%s' on device '%s' created.", poolName, devicePath))); vdoDevicePath = "/dev/mapper/" + poolName; } @@ -96,8 +94,8 @@ public ApiCallRc createDevicePool( apiCallRc.addEntries(createLVMPool(devicePaths, raidLevel, poolName)); break; case LVM_THIN: - apiCallRc.addEntries(createLVMPool(devicePaths, raidLevel, VG_PREFIX + poolName)); - apiCallRc.addEntries(createLVMThinPool(VG_PREFIX + poolName, poolName)); + apiCallRc.addEntries(createLVMPool(devicePaths, raidLevel, LvmThinDriverKind.VG_PREFIX + poolName)); + apiCallRc.addEntries(createLVMThinPool(LvmThinDriverKind.VG_PREFIX + poolName, poolName)); break; case ZFS_THIN: // no differentiation between ZFS and ZFS_THIN pool. fall-through case ZFS: @@ -132,18 +130,25 @@ private ApiCallRc createLVMPool(final List devicePaths, final RaidLevel for (final String devicePath : devicePaths) { LvmCommands.pvCreate(extCmdFactory.create(), devicePath); - apiCallRc.addEntry(ApiCallRcImpl.simpleEntry( - ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT, - String.format("PV for device '%s' created.", devicePath)) + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format("PV for device '%s' created.", devicePath) + ) + .putObjRef(ApiConsts.KEY_POOL_NAME, poolName) + .build() ); } LvmCommands.vgCreate(extCmdFactory.create(), poolName, raidLevel, devicePaths); - apiCallRc.addEntry(ApiCallRcImpl.simpleEntry( - ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT, - String.format("VG for devices [%s] with name '%s' created.", - String.join(", ", devicePaths), - poolName) + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format("VG for devices [%s] with name '%s' created.", + String.join(", ", devicePaths), + poolName) ) + .putObjRef(ApiConsts.KEY_POOL_NAME, poolName) + .build() ); } catch (StorageException storExc) @@ -155,6 +160,40 @@ private ApiCallRc createLVMPool(final List devicePaths, final RaidLevel return apiCallRc; } + private ApiCallRc deleteLVMPool(final List devicePaths, final String poolName) + { + ApiCallRcImpl apiCallRc = new ApiCallRcImpl(); + try + { + LvmCommands.vgRemove(extCmdFactory.create(), poolName); + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_DEL | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format("VG with name '%s' removed.", poolName) + ) + .putObjRef(ApiConsts.KEY_POOL_NAME, poolName) + .build() + ); + + LvmCommands.pvRemove(extCmdFactory.create(), devicePaths); + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_DEL | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format("PV for device(s) '%s' removed.", String.join(",", devicePaths)) + ) + .putObjRef(ApiConsts.KEY_POOL_NAME, poolName) + .build() + ); + } + catch (StorageException storExc) + { + errorReporter.reportError(storExc); + apiCallRc.addEntry(ApiCallRcImpl.copyFromLinstorExc(ApiConsts.FAIL_STOR_POOL_CONFIGURATION_ERROR, storExc)); + } + + return apiCallRc; + } + private ApiCallRc createLVMThinPool( final String lvmPoolName, final String thinPoolName @@ -168,9 +207,12 @@ private ApiCallRc createLVMThinPool( lvmPoolName, thinPoolName ); - apiCallRc.addEntry(ApiCallRcImpl.simpleEntry( - ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT, - String.format("Thin-pool '%s' in LVM-pool '%s' created.", thinPoolName, lvmPoolName))); + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format("Thin-pool '%s' in LVM-pool '%s' created.", thinPoolName, lvmPoolName) + ).putObjRef(ApiConsts.KEY_POOL_NAME, lvmPoolName + "/" + thinPoolName).build() + ); } catch (StorageException storExc) { @@ -180,6 +222,36 @@ private ApiCallRc createLVMThinPool( return apiCallRc; } + private ApiCallRc deleteLVMThinPool( + final List devicePaths, + final String lvmPoolName, + final String thinPoolName + ) + { + ApiCallRcImpl apiCallRc = new ApiCallRcImpl(); + try + { + LvmCommands.delete(extCmdFactory.create(), lvmPoolName, thinPoolName); + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_DEL | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format("Thin lv with name '%s' removed.", thinPoolName) + ) + .putObjRef(ApiConsts.KEY_POOL_NAME, lvmPoolName + "/" + thinPoolName) + .build() + ); + + apiCallRc.addEntries(deleteLVMPool(devicePaths, lvmPoolName)); + } + catch (StorageException storExc) + { + errorReporter.reportError(storExc); + apiCallRc.addEntry(ApiCallRcImpl.copyFromLinstorExc(ApiConsts.FAIL_STOR_POOL_CONFIGURATION_ERROR, storExc)); + } + + return apiCallRc; + } + private ApiCallRc createZPool( final List devicePaths, final RaidLevel raidLevel, @@ -196,12 +268,14 @@ private ApiCallRc createZPool( raidLevel, zPoolName ); - apiCallRc.addEntry(ApiCallRcImpl.simpleEntry( - ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT, - String.format( - "ZPool '%s' on device(s) [%s] created.", - zPoolName, - String.join(", ", devicePaths))) + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format( + "ZPool '%s' on device(s) [%s] created.", + zPoolName, + String.join(", ", devicePaths)) + ).putObjRef(ApiConsts.KEY_POOL_NAME, zPoolName).build() ); } catch (StorageException storExc) @@ -212,4 +286,72 @@ private ApiCallRc createZPool( return apiCallRc; } + + private ApiCallRc deleteZPool(final List devicePaths, final String zPoolName) + { + ApiCallRcImpl apiCallRc = new ApiCallRcImpl(); + + try + { + ZfsCommands.deleteZPool( + extCmdFactory.create(), + zPoolName + ); + apiCallRc.addEntry( + ApiCallRcImpl.entryBuilder( + ApiConsts.MASK_SUCCESS | ApiConsts.MASK_CRT | ApiConsts.MASK_PHYSICAL_DEVICE, + String.format( + "ZPool '%s' deleted.", + zPoolName) + ).putObjRef(ApiConsts.KEY_POOL_NAME, zPoolName).build() + ); + + Commands.wipeFs(extCmdFactory.create(), devicePaths); + } + catch (StorageException storExc) + { + errorReporter.reportError(storExc); + apiCallRc.addEntry(ApiCallRcImpl.copyFromLinstorExc(ApiConsts.FAIL_UNKNOWN_ERROR, storExc)); + } + return apiCallRc; + } + + public ApiCallRc deleteDevicePool( + final DeviceProviderKind deviceProviderKind, + final List devicePaths, + final String poolName + ) + { + ApiCallRcImpl apiCallRc = new ApiCallRcImpl(); + + switch (deviceProviderKind) + { + case LVM_THIN: + apiCallRc.addEntries(deleteLVMThinPool(devicePaths, LvmThinDriverKind.VG_PREFIX + poolName, poolName)); + break; + case LVM: + apiCallRc.addEntries(deleteLVMPool(devicePaths, poolName)); + break; + case ZFS_THIN: // no differentiation between ZFS and ZFS_THIN pool. fall-through + case ZFS: + apiCallRc.addEntries(deleteZPool(devicePaths, poolName)); + break; + + // the following cases make no sense, hence the fall-throughs + case DISKLESS: // fall-through + case FAIL_BECAUSE_NOT_A_VLM_PROVIDER_BUT_A_VLM_LAYER: // fall-through + case FILE: // fall-through + case FILE_THIN: // fall-through + default: + apiCallRc.addEntry( + ApiCallRcImpl.simpleEntry( + ApiConsts.FAIL_INVLD_PROVIDER, + "Delete device pool not supported for provider: " + deviceProviderKind + ) + ); + break; + } + + return apiCallRc; + } } diff --git a/satellite/src/main/java/com/linbit/linstor/storage/utils/LvmCommands.java b/satellite/src/main/java/com/linbit/linstor/storage/utils/LvmCommands.java index 5eac3ba55..0885fa446 100644 --- a/satellite/src/main/java/com/linbit/linstor/storage/utils/LvmCommands.java +++ b/satellite/src/main/java/com/linbit/linstor/storage/utils/LvmCommands.java @@ -12,6 +12,7 @@ import static com.linbit.linstor.storage.layer.provider.utils.Commands.genericExecutor; import java.io.File; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -436,6 +437,17 @@ public static OutputData pvCreate(ExtCmd extCmd, String devicePath) throws Stora ); } + public static OutputData pvRemove(ExtCmd extCmd, Collection devicePaths) throws StorageException + { + final String failMsg = "Failed to pvremove on device(s): " + String.join(", ", devicePaths); + return genericExecutor( + extCmd, + StringUtils.concat(new String[] {"pvremove"}, devicePaths), + failMsg, + failMsg + ); + } + public static OutputData vgCreate( ExtCmd extCmd, final String vgName, @@ -482,6 +494,22 @@ public static OutputData listPhysicalVolumes(ExtCmd extCmdRef, String volumeGrou ); } + + public static OutputData vgRemove( + ExtCmd extCmd, + final String vgName + ) + throws StorageException + { + final String failMsg = "Failed to vgremove on volume group: " + vgName; + return genericExecutor( + extCmd, + new String[]{"vgremove", vgName}, + failMsg, + failMsg + ); + } + private LvmCommands() { } diff --git a/satellite/src/main/java/com/linbit/linstor/storage/utils/ZfsCommands.java b/satellite/src/main/java/com/linbit/linstor/storage/utils/ZfsCommands.java index 6ce14377d..585d97b92 100644 --- a/satellite/src/main/java/com/linbit/linstor/storage/utils/ZfsCommands.java +++ b/satellite/src/main/java/com/linbit/linstor/storage/utils/ZfsCommands.java @@ -303,6 +303,24 @@ public static OutputData createZPool( ); } + public static OutputData deleteZPool( + ExtCmd extCmd, + final String zpoolName + ) throws StorageException + { + final String failMsg = "Failed to destroy zpool: " + zpoolName; + return genericExecutor( + extCmd, + new String[] { + "zpool", + "destroy", + zpoolName + }, + failMsg, + failMsg + ); + } + public static OutputData getPhysicalDevices(ExtCmd extCmdRef, String zPoolRef) throws StorageException { final String failMsg = "Failed to query physical devices for zpool: " + zPoolRef; diff --git a/server/proto/javainternal/c2s/MsgDeleteDevicePool.proto b/server/proto/javainternal/c2s/MsgDeleteDevicePool.proto new file mode 100644 index 000000000..eebb37b26 --- /dev/null +++ b/server/proto/javainternal/c2s/MsgDeleteDevicePool.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package com.linbit.linstor.proto.javainternal.c2s; +import "common/ProviderType.proto"; + +message MsgDeleteDevicePool { + repeated string device_paths = 1; + common.ProviderType provider_kind = 2; + string pool_name = 3; +} diff --git a/server/src/main/java/com/linbit/linstor/InternalApiConsts.java b/server/src/main/java/com/linbit/linstor/InternalApiConsts.java index 6b7e84401..be362c148 100644 --- a/server/src/main/java/com/linbit/linstor/InternalApiConsts.java +++ b/server/src/main/java/com/linbit/linstor/InternalApiConsts.java @@ -57,6 +57,7 @@ public class InternalApiConsts public static final String API_LIST_PHYSICAL_DEVICES = "ListPhysicalDevices"; public static final String API_ANSWER_PHYSICAL_DEVICES = "AnswerPhysicalDevices"; public static final String API_CREATE_DEVICE_POOL = "CreateDevicePool"; + public static final String API_DELETE_DEVICE_POOL = "DeleteDevicePool"; public static final long API_AUTH_ERROR_HOST_MISMATCH = 1; diff --git a/server/src/main/java/com/linbit/linstor/api/interfaces/serializer/CtrlStltSerializer.java b/server/src/main/java/com/linbit/linstor/api/interfaces/serializer/CtrlStltSerializer.java index 89270dd63..4cea9885b 100644 --- a/server/src/main/java/com/linbit/linstor/api/interfaces/serializer/CtrlStltSerializer.java +++ b/server/src/main/java/com/linbit/linstor/api/interfaces/serializer/CtrlStltSerializer.java @@ -121,5 +121,10 @@ CtrlStltSerializerBuilder createDevicePool( long vdoSlabSizeKib ); + CtrlStltSerializerBuilder deleteDevicePool( + List devicePaths, + DeviceProviderKind providerKindRef, + String poolName + ); } } diff --git a/server/src/main/java/com/linbit/linstor/api/protobuf/serializer/ProtoCtrlStltSerializerBuilder.java b/server/src/main/java/com/linbit/linstor/api/protobuf/serializer/ProtoCtrlStltSerializerBuilder.java index 120841ab1..b8dee5b5f 100644 --- a/server/src/main/java/com/linbit/linstor/api/protobuf/serializer/ProtoCtrlStltSerializerBuilder.java +++ b/server/src/main/java/com/linbit/linstor/api/protobuf/serializer/ProtoCtrlStltSerializerBuilder.java @@ -37,6 +37,7 @@ import com.linbit.linstor.proto.javainternal.c2s.IntStorPoolOuterClass.IntStorPool; import com.linbit.linstor.proto.javainternal.c2s.MsgCreateDevicePoolOuterClass; import com.linbit.linstor.proto.javainternal.c2s.MsgCreateDevicePoolOuterClass.MsgCreateDevicePool; +import com.linbit.linstor.proto.javainternal.c2s.MsgDeleteDevicePoolOuterClass.MsgDeleteDevicePool; import com.linbit.linstor.proto.javainternal.c2s.MsgIntApplyControllerOuterClass.MsgIntApplyController; import com.linbit.linstor.proto.javainternal.c2s.MsgIntApplyDeletedNodeOuterClass.MsgIntApplyDeletedNode; import com.linbit.linstor.proto.javainternal.c2s.MsgIntApplyDeletedRscOuterClass.MsgIntApplyDeletedRsc; @@ -724,6 +725,32 @@ public CtrlStltSerializer.CtrlStltSerializerBuilder createDevicePool( return this; } + @Override + public CtrlStltSerializer.CtrlStltSerializerBuilder deleteDevicePool( + List devicePaths, + DeviceProviderKind providerKindRef, + String poolName + ) + { + try + { + MsgDeleteDevicePool.Builder msgDeleteDevicePoolBuilder = + MsgDeleteDevicePool.newBuilder() + .addAllDevicePaths(devicePaths) + .setProviderKind(asProviderType(providerKindRef)) + .setPoolName(poolName); + + msgDeleteDevicePoolBuilder + .build() + .writeDelimitedTo(baos); + } + catch (IOException exc) + { + handleIOException(exc); + } + return this; + } + @Override public CtrlStltSerializer.CtrlStltSerializerBuilder physicalDevices(List entries) { diff --git a/server/src/main/java/com/linbit/linstor/storage/LvmThinDriverKind.java b/server/src/main/java/com/linbit/linstor/storage/LvmThinDriverKind.java index 9fe150276..965e2f1be 100644 --- a/server/src/main/java/com/linbit/linstor/storage/LvmThinDriverKind.java +++ b/server/src/main/java/com/linbit/linstor/storage/LvmThinDriverKind.java @@ -8,6 +8,8 @@ public class LvmThinDriverKind implements StorageDriverKind { + public static final String VG_PREFIX = "linstor_"; + @Override public String getDriverName() {