Skip to content

Commit

Permalink
rest/physicaldevice: Add "atomic" storage pool creation
Browse files Browse the repository at this point in the history
While creating a physical device pool, it is now possible to also
create a corresponding linstor storage pool.
if anything fails in that process before the storage pool is created,
the device pool will also be deleted again.
  • Loading branch information
rp- committed Dec 12, 2019
1 parent c8e3b2a commit 6a4d9a0
Show file tree
Hide file tree
Showing 14 changed files with 505 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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();
}

Expand Down Expand Up @@ -95,6 +102,33 @@ public void listPhysicalStorage(
});
}

private Map<String, String> deviceProviderToStorPoolProperty(DeviceProviderKind kind, String pool)
{
HashMap<String, String> 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}")
Expand All @@ -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<ApiCallRc> 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<String, String> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String, String> props = Collections.emptyMap();
}

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public static class PhysicalStorageCreate
{
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -151,6 +150,20 @@ private static List<LsBlkEntry> parsePhysicalDevices(ByteArrayInputStream inputS
}
}

public static String getDevicePoolName(final String devicePoolName, final List<String> 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<ApiCallRc> createDevicePool(
String nodeNameStr,
List<String> devicePaths,
Expand Down Expand Up @@ -202,14 +215,7 @@ private Flux<ApiCallRc> 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
{
Expand Down Expand Up @@ -242,6 +248,62 @@ private Flux<ApiCallRc> createDevicePoolInScope(
return response;
}

public Flux<ApiCallRc> deleteDevicePool(
String nodeNameStr,
List<String> 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<ApiCallRc> deleteDevicePoolInScope(
String nodeNameStr,
List<String> devicePaths,
DeviceProviderKind providerKindRef,
String poolName
)
{
Flux<ApiCallRc> 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<JsonGenTypes.PhysicalStorage> groupPhysicalStorageByDevice(
Map<NodeName, List<LsBlkEntry>> mapData
)
Expand Down
18 changes: 15 additions & 3 deletions docs/rest_v1_openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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'
2 changes: 1 addition & 1 deletion linstor-common
Submodule linstor-common updated from 5f9c78 to 3da619
Loading

0 comments on commit 6a4d9a0

Please sign in to comment.