From 749a3c537f597505087a939a33532aef3de90c78 Mon Sep 17 00:00:00 2001 From: Stan Bonev Date: Wed, 28 Nov 2018 11:04:03 -0500 Subject: RA: Release resources on specific target Change-Id: I695a916a3d527f61b9483bbb27977695fb1e094c Issue-ID: CCSDK-766 Signed-off-by: Stan Bonev --- .../ccsdk/sli/adaptors/ra/ResourceAllocator.java | 69 +++++++------ .../sli/adaptors/rm/comp/ReleaseFunction.java | 16 +-- .../sli/adaptors/rm/comp/ResourceManager.java | 4 + .../sli/adaptors/rm/comp/ResourceManagerImpl.java | 30 +++++- .../ccsdk/sli/adaptors/rm/dao/ResourceDao.java | 4 + .../sli/adaptors/rm/dao/jdbc/ResourceDaoImpl.java | 109 +++++++++------------ .../sli/adaptors/rm/dao/jdbc/ResourceJdbcDao.java | 4 + .../adaptors/rm/dao/jdbc/ResourceJdbcDaoImpl.java | 25 +++++ 8 files changed, 160 insertions(+), 101 deletions(-) (limited to 'resource-assignment/provider/src/main/java') diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/ra/ResourceAllocator.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/ra/ResourceAllocator.java index e0391bc14..5700cd72a 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/ra/ResourceAllocator.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/ra/ResourceAllocator.java @@ -48,7 +48,8 @@ public class ResourceAllocator implements SvcLogicResource { private static final Logger log = LoggerFactory.getLogger(ResourceAllocator.class); private static final String[] INPUT_PREFIX = {"ra-input.", "tmp.resource-allocator."}; - private static final String START_RELEASE_LC = "Starting release for: {}"; + private static final String START_RELEASE = "Starting release for: {}"; + private static final String START_RELEASE_FOR_TARGET = "Starting release for: {} on target: {}"; private ResourceManager resourceManager; private EndPointAllocator endPointAllocator; @@ -201,6 +202,10 @@ public class ResourceAllocator implements SvcLogicResource { getParam(ctx, new String[] {"reservation-entity-type", "resource-entity-type"}, true, null); String resourceEntityVersion = getParam(ctx, new String[] {"reservation-entity-version", "resource-entity-version"}, false, null); + String resourceTargetId = + getParam(ctx, new String[] {"reservation-target-id", "resource-target-id"}, false, null); + String resourceTargetType = + getParam(ctx, new String[] {"reservation-target-type", "resource-target-type"}, false, null); String endPointPosition = getParam(ctx, "endpoint-position", false, null); @@ -212,8 +217,12 @@ public class ResourceAllocator implements SvcLogicResource { ResourceRequest rr = new ResourceRequest(); rr.endPointPosition = endPointPosition; + ResourceTarget rt = new ResourceTarget(); + rt.resourceTargetType = resourceTargetType; + rt.resourceTargetId = resourceTargetId; + try { - this.release(sd, rr); + this.release(sd, rr, rt); } catch (Exception e) { throw new SvcLogicException(e.getMessage()); } @@ -221,54 +230,54 @@ public class ResourceAllocator implements SvcLogicResource { } public AllocationStatus release(ResourceEntity sd) throws Exception { - - if (sd.resourceEntityVersion != null) { - String resourceSet = sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + sd.resourceEntityVersion; - log.info(START_RELEASE_LC, resourceSet); - - resourceManager.releaseResourceSet(resourceSet); - } else { - String resourceUnion = sd.resourceEntityType + "::" + sd.resourceEntityId; - log.info(START_RELEASE_LC, resourceUnion); - - resourceManager.releaseResourceUnion(resourceUnion); - } - - return AllocationStatus.Success; - + return release(sd, null, null); } public AllocationStatus release(ResourceEntity sd, ResourceRequest rr) throws Exception { + return release(sd, rr, null); + } + + public AllocationStatus release(ResourceEntity sd, ResourceRequest rr, ResourceTarget rt) throws Exception { if (sd != null && sd.resourceEntityVersion != null) { + String resourceSet = null; + if (rr != null && rr.endPointPosition != null && !rr.endPointPosition.isEmpty()) { - String resourceSet = sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + rr.endPointPosition - + "::" + sd.resourceEntityVersion; - log.info(START_RELEASE_LC, resourceSet); - resourceManager.releaseResourceSet(resourceSet); + resourceSet = sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + rr.endPointPosition + "::" + + sd.resourceEntityVersion; + } else { + resourceSet = sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + sd.resourceEntityVersion; + } + if (rt != null && rt.resourceTargetId != null && rt.resourceTargetType != null) { + String assetId = rt.resourceTargetType + "::" + rt.resourceTargetId; + log.info(START_RELEASE_FOR_TARGET, resourceSet, assetId); + resourceManager.releaseResourceSet(resourceSet, assetId); } else { - String resourceSet = - sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + sd.resourceEntityVersion; - log.info(START_RELEASE_LC, resourceSet); + log.info(START_RELEASE, resourceSet); resourceManager.releaseResourceSet(resourceSet); } } else if (sd != null && (sd.resourceEntityVersion == null || sd.resourceEntityVersion.isEmpty())) { + String resourceUnion = null; + if (rr != null && rr.endPointPosition != null && !rr.endPointPosition.isEmpty()) { - String resourceUnion = sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + rr.endPointPosition; - log.info(START_RELEASE_LC, resourceUnion); - resourceManager.releaseResourceUnion(resourceUnion); + resourceUnion = sd.resourceEntityType + "::" + sd.resourceEntityId + "::" + rr.endPointPosition; + } else { + resourceUnion = sd.resourceEntityType + "::" + sd.resourceEntityId; + } + if (rt != null && rt.resourceTargetId != null && rt.resourceTargetType != null) { + String assetId = rt.resourceTargetType + "::" + rt.resourceTargetId; + log.info(START_RELEASE_FOR_TARGET, resourceUnion, assetId); + resourceManager.releaseResourceUnion(resourceUnion, assetId); } else { - String resourceUnion = sd.resourceEntityType + "::" + sd.resourceEntityId; - log.info(START_RELEASE_LC, resourceUnion); + log.info(START_RELEASE, resourceUnion); resourceManager.releaseResourceUnion(resourceUnion); } } return AllocationStatus.Success; - } private QueryStatus allocateResources(SvcLogicContext ctx, boolean checkOnly, String prefix) diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ReleaseFunction.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ReleaseFunction.java index c0dec8bb7..17641845f 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ReleaseFunction.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ReleaseFunction.java @@ -24,7 +24,6 @@ package org.onap.ccsdk.sli.adaptors.rm.comp; import java.util.Collection; import java.util.Iterator; import java.util.List; - import org.onap.ccsdk.sli.adaptors.lock.comp.LockHelper; import org.onap.ccsdk.sli.adaptors.lock.comp.ResourceLockedException; import org.onap.ccsdk.sli.adaptors.lock.comp.SynchronizedFunction; @@ -42,21 +41,24 @@ class ReleaseFunction extends SynchronizedFunction { private ResourceDao resourceDao; - private String resourceSetId, resourceUnionId; + private String resourceSetId, resourceUnionId, assetId; - public ReleaseFunction(LockHelper lockHelper, ResourceDao resourceDao, String resourceSetId, - String resourceUnionId, Collection lockNames, int lockTimeout) { + public ReleaseFunction(LockHelper lockHelper, ResourceDao resourceDao, String resourceSetId, String resourceUnionId, + String assetId, Collection lockNames, int lockTimeout) { super(lockHelper, lockNames, lockTimeout); this.resourceDao = resourceDao; this.resourceSetId = resourceSetId; this.resourceUnionId = resourceUnionId; + this.assetId = assetId; } @Override public void _exec() throws ResourceLockedException { - List resourceList = - resourceSetId != null - ? resourceDao.getResourceSet(resourceSetId) : resourceDao.getResourceUnion(resourceUnionId); + List resourceList = assetId != null + ? (resourceSetId != null ? resourceDao.getResourceSetForAsset(resourceSetId, assetId) + : resourceDao.getResourceUnionForAsset(resourceUnionId, assetId)) + : (resourceSetId != null ? resourceDao.getResourceSet(resourceSetId) + : resourceDao.getResourceUnion(resourceUnionId)); for (Resource r : resourceList) { boolean updated = false; if (r.allocationItems != null) { diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManager.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManager.java index ee77dee96..68d251587 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManager.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManager.java @@ -38,6 +38,10 @@ public interface ResourceManager { void releaseResourceUnion(String resourceUnionId); + void releaseResourceSet(String resourceSetId, String assetId); + + void releaseResourceUnion(String resourceUnionId, String assetId); + Resource queryResource(String resourceName, String assetId, String resourceUnionFilter, String resourceShareGroupFilter); diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManagerImpl.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManagerImpl.java index 9ba88912d..c4d53ce70 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManagerImpl.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/comp/ResourceManagerImpl.java @@ -88,7 +88,7 @@ public class ResourceManagerImpl implements ResourceManager { Set lockNames = getLockNames(resourceList); ReleaseFunction releaseFunction = - new ReleaseFunction(lockHelper, resourceDao, resourceSetId, null, lockNames, lockTimeout); + new ReleaseFunction(lockHelper, resourceDao, resourceSetId, null, null, lockNames, lockTimeout); releaseFunction.exec(); } @@ -101,7 +101,33 @@ public class ResourceManagerImpl implements ResourceManager { Set lockNames = getLockNames(resourceList); ReleaseFunction releaseFunction = - new ReleaseFunction(lockHelper, resourceDao, null, resourceUnionId, lockNames, lockTimeout); + new ReleaseFunction(lockHelper, resourceDao, null, resourceUnionId, null, lockNames, lockTimeout); + releaseFunction.exec(); + } + + @Override + public void releaseResourceSet(String resourceSetId, String assetId) { + List resourceList = resourceDao.getResourceSetForAsset(resourceSetId, assetId); + if (resourceList == null || resourceList.isEmpty()) { + return; + } + + Set lockNames = getLockNames(resourceList); + ReleaseFunction releaseFunction = + new ReleaseFunction(lockHelper, resourceDao, resourceSetId, null, assetId, lockNames, lockTimeout); + releaseFunction.exec(); + } + + @Override + public void releaseResourceUnion(String resourceUnionId, String assetId) { + List resourceList = resourceDao.getResourceUnionForAsset(resourceUnionId, assetId); + if (resourceList == null || resourceList.isEmpty()) { + return; + } + + Set lockNames = getLockNames(resourceList); + ReleaseFunction releaseFunction = + new ReleaseFunction(lockHelper, resourceDao, null, resourceUnionId, assetId, lockNames, lockTimeout); releaseFunction.exec(); } diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/ResourceDao.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/ResourceDao.java index 57c3e938e..87baf26c1 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/ResourceDao.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/ResourceDao.java @@ -36,6 +36,10 @@ public interface ResourceDao { List getResourceUnion(String resourceUnionId); + List getResourceSetForAsset(String resourceSetId, String assetId); + + List getResourceUnionForAsset(String resourceUnionId, String assetId); + Resource query(String assetId, String resourceName, String resourceUnionFilter, String resourceShareGroupFilter); List query(String assetIdFilter, String resourceName); diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceDaoImpl.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceDaoImpl.java index 172651631..21f5575fc 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceDaoImpl.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceDaoImpl.java @@ -44,25 +44,7 @@ public class ResourceDaoImpl implements ResourceDao { @Override public org.onap.ccsdk.sli.adaptors.rm.data.Resource getResource(String assetId, String resourceName) { Resource rEntity = resourceJdbcDao.getResource(assetId, resourceName); - org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResource(rEntity); - - if (r != null) { - List aiEntityList = allocationItemJdbcDao.getAllocationItems(rEntity.id); - r.allocationItems = new ArrayList<>(); - for (AllocationItem aiEntity : aiEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.AllocationItem ai = createAllocationItem(r, aiEntity); - r.allocationItems.add(ai); - } - - List rlEntityList = resourceLoadJdbcDao.getResourceLoads(rEntity.id); - r.resourceLoadList = new ArrayList<>(); - for (ResourceLoad rlEntity : rlEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.ResourceLoad rl = createResourceLoad(r, rlEntity); - r.resourceLoadList.add(rl); - } - } - - return r; + return createResourceWithItems(rEntity); } @Override @@ -96,22 +78,8 @@ public class ResourceDaoImpl implements ResourceDao { List rEntityList = resourceJdbcDao.queryResources(assetIdFilter, resourceName); List rlist = new ArrayList<>(); for (Resource rEntity : rEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResource(rEntity); + org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResourceWithItems(rEntity); rlist.add(r); - - List aiEntityList = allocationItemJdbcDao.getAllocationItems(rEntity.id); - r.allocationItems = new ArrayList<>(); - for (AllocationItem aiEntity : aiEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.AllocationItem ai = createAllocationItem(r, aiEntity); - r.allocationItems.add(ai); - } - - List rlEntityList = resourceLoadJdbcDao.getResourceLoads(rEntity.id); - r.resourceLoadList = new ArrayList<>(); - for (ResourceLoad rlEntity : rlEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.ResourceLoad rl = createResourceLoad(r, rlEntity); - r.resourceLoadList.add(rl); - } } return rlist; } @@ -254,22 +222,8 @@ public class ResourceDaoImpl implements ResourceDao { List rEntityList = resourceJdbcDao.getResourceSet(resourceSetId); List rlist = new ArrayList<>(); for (Resource rEntity : rEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResource(rEntity); + org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResourceWithItems(rEntity); rlist.add(r); - - List aiEntityList = allocationItemJdbcDao.getAllocationItems(rEntity.id); - r.allocationItems = new ArrayList<>(); - for (AllocationItem aiEntity : aiEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.AllocationItem ai = createAllocationItem(r, aiEntity); - r.allocationItems.add(ai); - } - - List rlEntityList = resourceLoadJdbcDao.getResourceLoads(rEntity.id); - r.resourceLoadList = new ArrayList<>(); - for (ResourceLoad rlEntity : rlEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.ResourceLoad rl = createResourceLoad(r, rlEntity); - r.resourceLoadList.add(rl); - } } return rlist; } @@ -279,22 +233,32 @@ public class ResourceDaoImpl implements ResourceDao { List rEntityList = resourceJdbcDao.getResourceUnion(resourceUnionId); List rlist = new ArrayList<>(); for (Resource rEntity : rEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResource(rEntity); + org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResourceWithItems(rEntity); rlist.add(r); + } + return rlist; + } - List aiEntityList = allocationItemJdbcDao.getAllocationItems(rEntity.id); - r.allocationItems = new ArrayList<>(); - for (AllocationItem aiEntity : aiEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.AllocationItem ai = createAllocationItem(r, aiEntity); - r.allocationItems.add(ai); - } + @Override + public List getResourceSetForAsset(String resourceSetId, + String assetId) { + List rEntityList = resourceJdbcDao.getResourceSetForAsset(resourceSetId, assetId); + List rlist = new ArrayList<>(); + for (Resource rEntity : rEntityList) { + org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResourceWithItems(rEntity); + rlist.add(r); + } + return rlist; + } - List rlEntityList = resourceLoadJdbcDao.getResourceLoads(rEntity.id); - r.resourceLoadList = new ArrayList<>(); - for (ResourceLoad rlEntity : rlEntityList) { - org.onap.ccsdk.sli.adaptors.rm.data.ResourceLoad rl = createResourceLoad(r, rlEntity); - r.resourceLoadList.add(rl); - } + @Override + public List getResourceUnionForAsset(String resourceUnionId, + String assetId) { + List rEntityList = resourceJdbcDao.getResourceUnionForAsset(resourceUnionId, assetId); + List rlist = new ArrayList<>(); + for (Resource rEntity : rEntityList) { + org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResourceWithItems(rEntity); + rlist.add(r); } return rlist; } @@ -374,6 +338,27 @@ public class ResourceDaoImpl implements ResourceDao { } } + private org.onap.ccsdk.sli.adaptors.rm.data.Resource createResourceWithItems(Resource rEntity) { + org.onap.ccsdk.sli.adaptors.rm.data.Resource r = createResource(rEntity); + + if (r != null) { + List aiEntityList = allocationItemJdbcDao.getAllocationItems(rEntity.id); + r.allocationItems = new ArrayList<>(); + for (AllocationItem aiEntity : aiEntityList) { + org.onap.ccsdk.sli.adaptors.rm.data.AllocationItem ai = createAllocationItem(r, aiEntity); + r.allocationItems.add(ai); + } + + List rlEntityList = resourceLoadJdbcDao.getResourceLoads(rEntity.id); + r.resourceLoadList = new ArrayList<>(); + for (ResourceLoad rlEntity : rlEntityList) { + org.onap.ccsdk.sli.adaptors.rm.data.ResourceLoad rl = createResourceLoad(r, rlEntity); + r.resourceLoadList.add(rl); + } + } + return r; + } + private org.onap.ccsdk.sli.adaptors.rm.data.Resource createResource(Resource resourceEntity) { if (resourceEntity == null) { return null; diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDao.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDao.java index fe19fd905..d4d9a0543 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDao.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDao.java @@ -33,6 +33,10 @@ public interface ResourceJdbcDao { List getResourceUnion(String resourceUnionId); + List getResourceSetForAsset(String resourceSetId, String assetId); + + List getResourceUnionForAsset(String resourceUnionId, String assetId); + void add(Resource r); void delete(long id); diff --git a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDaoImpl.java b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDaoImpl.java index 9283df9ea..c94a5d8a0 100644 --- a/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDaoImpl.java +++ b/resource-assignment/provider/src/main/java/org/onap/ccsdk/sli/adaptors/rm/dao/jdbc/ResourceJdbcDaoImpl.java @@ -50,6 +50,12 @@ public class ResourceJdbcDaoImpl implements ResourceJdbcDao { private static final String RESOURCE_UNION_SQL = "SELECT * FROM RESOURCE WHERE resource_id IN (\n" + "SELECT DISTINCT resource_id FROM ALLOCATION_ITEM WHERE resource_union_id = ?)"; + private static final String RESOURCE_SET_FOR_ASSET_SQL = "SELECT * FROM RESOURCE WHERE resource_id IN (\n" + + "SELECT DISTINCT resource_id FROM ALLOCATION_ITEM WHERE resource_set_id = ?) AND asset_id = ?"; + + private static final String RESOURCE_UNION_FOR_ASSET_SQL = "SELECT * FROM RESOURCE WHERE resource_id IN (\n" + + "SELECT DISTINCT resource_id FROM ALLOCATION_ITEM WHERE resource_union_id = ?) AND asset_id = ?"; + private static final String INSERT_SQL = "INSERT INTO RESOURCE (\n" + " asset_id, resource_name, resource_type, lt_used, ll_label, ll_reference_count, rr_used)\n" + "VALUES (?, ?, ?, ?, ?, ?, ?)"; @@ -102,6 +108,25 @@ public class ResourceJdbcDaoImpl implements ResourceJdbcDao { return jdbcTemplate.query(RESOURCE_UNION_SQL, new Object[] {resourceUnionId}, resourceRowMapper); } + @Override + public List getResourceSetForAsset(String resourceSetId, String assetId) { + if (resourceSetId == null) { + return Collections.emptyList(); + } + + return jdbcTemplate.query(RESOURCE_SET_FOR_ASSET_SQL, new Object[] {resourceSetId, assetId}, resourceRowMapper); + } + + @Override + public List getResourceUnionForAsset(String resourceUnionId, String assetId) { + if (resourceUnionId == null) { + return Collections.emptyList(); + } + + return jdbcTemplate.query(RESOURCE_UNION_FOR_ASSET_SQL, new Object[] {resourceUnionId, assetId}, + resourceRowMapper); + } + @Override public void add(final Resource r) { PreparedStatementCreator psc = dbc -> { -- cgit 1.2.3-korg