/** * Copyright 2013-2015 Seagate Technology LLC. * * This Source Code Form is subject to the terms of the Mozilla * Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at * https://mozilla.org/MP:/2.0/. * * This program is distributed in the hope that it will be useful, * but is provided AS-IS, WITHOUT ANY WARRANTY; including without * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public * License for more details. * * See www.openkinetic.org for more project information */ package com.seagate.kinetic.simulator.internal; import java.util.logging.Logger; import kinetic.client.KineticException; import kinetic.simulator.SimulatorConfiguration; import com.google.protobuf.ByteString; import com.seagate.kinetic.common.lib.KineticMessage; import com.seagate.kinetic.common.lib.MessageDigestUtil; import com.seagate.kinetic.proto.Kinetic.Command; import com.seagate.kinetic.proto.Kinetic.Command.Algorithm; import com.seagate.kinetic.proto.Kinetic.Command.MessageType; import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL.Permission; import com.seagate.kinetic.proto.Kinetic.Command.Status.StatusCode; import com.seagate.kinetic.simulator.persist.KVValue; import com.seagate.kinetic.simulator.persist.Store; import com.seagate.kinetic.simulator.persist.memory.KeyComparator; /** * * Back ground operation handler prototype implementation * <p> * The current implementation responds to the request with a SUCCESS status if it passed * minimal verifications. * * @author chiaming * */ public abstract class BackGroundOpHandler { private final static Logger logger = Logger.getLogger(BackGroundOpHandler.class .getName()); @SuppressWarnings({ "rawtypes" }) public static void mediaScan(KineticMessage request, KineticMessage respond, SimulatorEngine engine) throws KVStoreException, KineticException { KeyComparator comparator = new KeyComparator(); Command.Builder commandBuilder = (Command.Builder) respond.getCommand(); // set reply type commandBuilder.getHeaderBuilder().setMessageType( MessageType.MEDIASCAN_RESPONSE); // set ack sequence commandBuilder.getHeaderBuilder().setAckSequence( request.getCommand().getHeader().getSequence()); // max return key count int maxReturned = request.getCommand().getBody().getRange() .getMaxReturned(); int MaxSupported = SimulatorConfiguration.getMaxSupportedKeyRangeSize(); try { // if not set, set to default if (maxReturned <= 0) { maxReturned = MaxSupported; } else if (maxReturned > MaxSupported) { throw new InvalidRequestException( "Exceed max returned key range., max allowed=" + SimulatorConfiguration .getMaxSupportedKeyRangeSize() + ", request=" + maxReturned); } // check message type checkIsMessageValid (request); // check permission checkPermission (request, engine); Store store = engine.getStore(); /** * XXX 09/09/2014 chiaming: * framework to start background operation * the job should be stoppable by a higher priority received * before/after the long running bg ops. * * The following statements are for testing purpose only */ // get start key ByteString startKey = request.getCommand().getBody().getRange() .getStartKey(); // get end key ByteString endKey = request.getCommand().getBody().getRange() .getEndKey(); byte[] endKeybytes = endKey.toByteArray(); // if scan to the end of the map boolean toEndOfMap = false; if (endKey.isEmpty()) { toEndOfMap = true; } // finish scan flag boolean done = false; // kv entry KVValue kv = null; // check if start key inclusive if (request.getCommand().getBody().getRange() .getStartKeyInclusive()) { // include start key kv = get(store, startKey); if (kv == null) { kv = getNext(store, startKey); } } else { // get next key kv = getNext(store, startKey); } // scan the drive long index = 0; while (done == false) { if (kv != null) { // get algo Algorithm algo = kv.getAlgorithm(); // get tag ByteString tag = kv.getTag(); // compare tag logger.info((index++) + ": scan media for key: " + kv.getKeyOf() + ", algo: " + algo); if ((tag != null) && (tag.isEmpty() == false) && MessageDigestUtil .isSupportedForKineticJava(algo)) { ByteString ctag = MessageDigestUtil.calculateTag(algo, kv.getData() .toByteArray()); if (tag.equals(ctag) == false) { logger.info("tag does not match for key: " + kv.getKeyOf() + ", algo: " + algo); if (commandBuilder.getBodyBuilder() .getRangeBuilder().getKeysCount() < maxReturned) { // add bad key commandBuilder.getBodyBuilder() .getRangeBuilder() .addKeys(kv.getKeyOf()); } else { // reached max returned keys // set endkey in response commandBuilder.getBodyBuilder() .getRangeBuilder() .setEndKey(kv.getKeyOf()); // finished scan return; } } else { logger.info("tag validated for key: " + kv.getKeyOf() + ", algo: " + algo); } } // read next key kv = getNext(store, kv.getKeyOf()); if (kv == null) { // reached to end of map done = true; } else if (toEndOfMap == false) { /** * check if passed end key */ if (comparator.compare(endKeybytes, kv.getKeyOf() .toByteArray()) < 0) { done = true; } } } else { logger.info(index + ": scan media reached end of map"); done = true; } } // set endkey in response commandBuilder .getBodyBuilder() .getRangeBuilder() .setEndKey( request.getCommand().getBody().getRange() .getEndKey()); } catch (KVSecurityException se) { commandBuilder.getStatusBuilder() .setCode(StatusCode.NOT_AUTHORIZED); commandBuilder.getStatusBuilder().setStatusMessage(se.getMessage()); logger.warning("unauthorized media scan opeartion request"); } catch (InvalidRequestException ire) { commandBuilder.getStatusBuilder().setCode( StatusCode.INVALID_REQUEST); commandBuilder.getStatusBuilder() .setStatusMessage(ire.getMessage()); } } @SuppressWarnings("unchecked") public static void mediaOptimize(KineticMessage request, KineticMessage respond, SimulatorEngine engine) throws KVStoreException, KineticException { Command.Builder commandBuilder = (Command.Builder) respond.getCommand(); // set reply type commandBuilder.getHeaderBuilder().setMessageType( MessageType.MEDIAOPTIMIZE_RESPONSE); // set ack sequence commandBuilder.getHeaderBuilder().setAckSequence( request.getCommand().getHeader().getSequence()); try { checkIsMessageValid (request); // check permission checkPermission (request, engine); /** * XXX 09/11/2014 chiaming: * framework to start media optimize op * the job should be stoppable by a higher priority received * before/after the long running bg ops. * * The following statements are for testing purpose only */ // get start key ByteString startKey = request.getCommand().getBody().getRange() .getStartKey(); // get end key ByteString endKey = request.getCommand().getBody().getRange() .getEndKey(); // ask store to do media compaction engine.getStore().compactRange(startKey, endKey); // set endkey in response commandBuilder .getBodyBuilder() .getRangeBuilder() .setEndKey( request.getCommand().getBody().getRange() .getEndKey()); } catch (KVSecurityException se) { commandBuilder.getStatusBuilder() .setCode(StatusCode.NOT_AUTHORIZED); commandBuilder.getStatusBuilder().setStatusMessage(se.getMessage()); logger.warning("unauthorized media optimize opeartion request"); } catch (InvalidRequestException ire) { commandBuilder.getStatusBuilder().setCode( StatusCode.INVALID_REQUEST); commandBuilder.getStatusBuilder() .setStatusMessage(ire.getMessage()); } } private static void checkIsMessageValid (KineticMessage request) throws InvalidRequestException { MessageType mtype = request.getCommand().getHeader().getMessageType(); switch (mtype) { case MEDIASCAN: case MEDIAOPTIMIZE: // XXX: more request message validation here return; default: throw new InvalidRequestException ("not a valid back ground op type: " + mtype.name()); } } /** * check if user has range permission. * * @param request * @param engine * @throws KVSecurityException */ private static void checkPermission (KineticMessage request, SimulatorEngine engine) throws KVSecurityException { ByteString startKey = request.getCommand().getBody().getRange() .getStartKey(); ByteString endKey = request.getCommand().getBody().getRange() .getEndKey(); long user = request.getMessage().getHmacAuth().getIdentity(); boolean hasPermission = Authorizer.hasRangePermission( engine.getAclMap(), user, Permission.RANGE, startKey, endKey); if (hasPermission == false) { throw new KVSecurityException( "no permission for the requested range: " + request.getCommand()); } else { logger.info("range permission validate: " + request.getCommand()); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private static KVValue get(Store store, ByteString key) { KVValue kv = null; try { kv = (KVValue) store.get(key); } catch (Exception e) { logger.info(e.getMessage()); } return kv; } @SuppressWarnings({ "unchecked", "rawtypes" }) private static KVValue getNext(Store store, ByteString key) { KVValue kv = null; try { kv = (KVValue) store.getNext(key); } catch (KVStoreException e) { logger.info("no next key found for getNext"); } return kv; } }