// $Id: HsmFlushControlManager.java,v 1.6 2006-07-31 16:35:50 patrick Exp $ package diskCacheV111.hsmControl.flush ; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; import diskCacheV111.pools.PoolCellInfo; import diskCacheV111.pools.PoolCostInfo; import diskCacheV111.pools.StorageClassFlushInfo; import diskCacheV111.vehicles.PoolFlushControlInfoMessage; import diskCacheV111.vehicles.PoolFlushDoFlushMessage; import diskCacheV111.vehicles.PoolFlushGainControlMessage; import diskCacheV111.vehicles.PoolManagerPoolModeMessage; import diskCacheV111.vehicles.PoolStatusChangedMessage; import dmg.cells.nucleus.CellAdapter; import dmg.cells.nucleus.CellAddressCore; import dmg.cells.nucleus.CellInfo; import dmg.cells.nucleus.CellMessage; import dmg.cells.nucleus.CellNucleus; import dmg.cells.nucleus.CellPath; import dmg.cells.nucleus.NoRouteToCellException; import org.dcache.cells.CellStub; import org.dcache.util.Args; import static com.google.common.base.Preconditions.checkArgument; import static java.util.concurrent.TimeUnit.SECONDS; public class HsmFlushControlManager extends CellAdapter { private static final Logger _log = LoggerFactory.getLogger(HsmFlushControlManager.class); private final CellNucleus _nucleus; private int _requests; private int _failed; private File _database; private String _status = "init" ; private static final boolean LOG_ENABLED = true ; private final PoolCollector _poolCollector; private final Set<String> _poolGroupList = new HashSet<>() ; private long _getPoolCollectionTicker = 2L * 60L * 1000L ; private long _timerInterval = 30L * 1000L ; private final FlushController _flushController = new FlushController() ; private final EventDispatcher _eventDispatcher = new EventDispatcher() ; private QueueWatch _queueWatch; private Map<String,Object> _properties = new HashMap<>() ; private final Object _propertyLock = new Object() ; private long _propertiesUpdated; private CellStub _poolManager; /** * Usage : ... [options] <pgroup0> [<pgroup1>[...]] * Options : * -scheduler=<driver> * -poolCollectionUpdate=<timeInMinutes> * -gainControlUpdate=<timeInMinutes> * -timer=<timeInSeconds> * * Options are forwarded to the driver as well. */ public HsmFlushControlManager(String name, String args) { super(name, args); _nucleus = getNucleus(); _poolCollector = new PoolCollector(); } @Override protected void starting() throws Exception { _poolManager = new CellStub(this, new CellPath("PoolManager"), 30, SECONDS); Args args = getArgs(); checkArgument(args.argc() >= 1, "Usage : ... <pgroup0> [<pgroup1>[...]]"); for (int i = 0, n = args.argc(); i < n; i++) { _poolGroupList.add(args.argv(0)); args.shift(); } String tmp = args.getOpt("scheduler"); if ((tmp != null) && (!tmp.isEmpty())) { _eventDispatcher.loadHandler(tmp, false, args); } _queueWatch = new QueueWatch(); tmp = args.getOpt("poolCollectionUpdate"); if ((tmp != null) && (!tmp.isEmpty())) { try { _getPoolCollectionTicker = Long.parseLong(tmp) * 60L * 1000L; } catch (Exception ee) { _log.warn("Illegal value for poolCollectionUpdate : {} (choosing {} millis)", tmp, _getPoolCollectionTicker); } } tmp = args.getOpt("gainControlUpdate"); if ((tmp != null) && (!tmp.isEmpty())) { long gainControlTicker = 0L; try { _queueWatch.setGainControlTicker(gainControlTicker = Long.parseLong(tmp) * 60L * 1000L); } catch (Exception ee) { _log.warn( "Illegal value for gainControlUpdate : {} (choosing {} millis)", tmp, gainControlTicker); } } tmp = args.getOpt("timer"); if ((tmp != null) && (!tmp.isEmpty())) { try { _timerInterval = Long.parseLong(tmp) * 1000L; } catch (Exception ee) { _log.warn("Illegal value for timer : {} (choosing {} millis)", tmp, _timerInterval); } } useInterpreter(true); } @Override protected void started() { _nucleus.newThread( _queueWatch , "queueWatch").start() ; } private class QueueWatch implements Runnable { private long _gainControlTicker = 1L * 60L * 1000L ; private long next_getPoolCollection ; private long next_sendGainControl ; private long next_timerEvent ; private synchronized void setGainControlTicker( long gainControl ){ _gainControlTicker = gainControl ; triggerGainControl() ; } @Override public void run(){ _log.info("QueueWatch started" ) ; try{ _log.info("QueueWatch : waiting awhile before starting"); Thread.sleep(10000); }catch(InterruptedException ie ){ _log.warn("QueueWatch: interrupted during initial wait. Stopping"); return ; } _status = "Running Collector" ; _poolCollector.runCollector( _poolGroupList ) ; long now = System.currentTimeMillis() ; next_getPoolCollection = now + 0 ; next_sendGainControl = now + _gainControlTicker ; next_timerEvent = now + _timerInterval ; while( ! Thread.interrupted() ){ synchronized( this ){ _status = "Sleeping" ; try{ wait(10000); }catch(InterruptedException ie ){ break ; } now = System.currentTimeMillis() ; if( now > next_getPoolCollection ){ _status = "Running Collector" ; _poolCollector.runCollector( _poolGroupList ) ; next_getPoolCollection = now + _getPoolCollectionTicker ; } if( now > next_sendGainControl ){ _status = "Sending Flush Controls" ; _flushController.sendFlushControlMessages( 2L * _gainControlTicker ) ; next_sendGainControl = now + _gainControlTicker ; } if( now > next_timerEvent ){ _eventDispatcher.timer(); next_timerEvent = now + _timerInterval ; } } } _log.info( "QueueWatch stopped" ) ; } public synchronized void triggerGainControl(){ next_sendGainControl = System.currentTimeMillis() - 10 ; notifyAll(); } public synchronized void triggerGetPoolCollection(){ next_getPoolCollection = System.currentTimeMillis() - 10 ; notifyAll(); } } private class FlushController { // // sends 'gain control' messages to all clients we want // to control. // private List<String> _poolList; private boolean _control = true ; private synchronized void updatePoolList( Collection<String> c ){ _poolList = new ArrayList<>(c); } private synchronized void setControl( boolean control ){ _control = control ; } private void sendFlushControlMessages( long gainControlInterval ){ List<String> poolList; synchronized( this ){ poolList = _poolList ; } if( poolList == null ) { return; } for (String poolName : poolList) { PoolFlushGainControlMessage msg = new PoolFlushGainControlMessage( poolName, _control ? gainControlInterval : 0L); if (LOG_ENABLED) { _log.info("sendFlushControlMessage : sending PoolFlushGainControlMessage to " + poolName); } sendMessage(new CellMessage(new CellPath(poolName), msg)); } } } public class HFCFlushInfo implements HsmFlushControlCore.FlushInfo { private StorageClassFlushInfo _flushInfo, _previousFlushInfo; private boolean _flushingRequested; private boolean _flushingPending; private int _flushingError; private Object _flushingErrorObj; private long _flushingId; private final HFCPool _pool; private final String _name; public HFCFlushInfo( HFCPool pool , StorageClassFlushInfo flush ){ _flushInfo = flush ; _pool = pool ; _name = _flushInfo.getStorageClass()+"@"+_flushInfo.getHsm() ; } @Override public synchronized String getName(){ return _name ; } public HsmFlushControlCore.FlushInfoDetails getDetails(){ HsmFlushControllerFlushInfoDetails details = new HsmFlushControllerFlushInfoDetails() ; details._name = _name ; details._isFlushing = isFlushing() ; details._flushInfo = _flushInfo ; return details ; } private synchronized void updateFlushInfo( StorageClassFlushInfo flush ){ _previousFlushInfo = _flushInfo ; _flushInfo = flush ; } @Override public synchronized boolean isFlushing(){ return _flushingRequested || _flushingPending ; } @Override public synchronized void flush( int count ) throws Exception { flushStorageClass( _pool._poolName , _name , count ) ; _flushingRequested = true ; _flushingPending = false ; _flushingError = 0 ; } public synchronized void setFlushingAck( long id ){ _flushingPending = true ; _flushingId = id ; } public synchronized void setFlushingFailed( int errorCode , Object errorObject ){ _flushingPending = false ; _flushingRequested = false ; _flushingId = 0L ; _flushingError = errorCode ; _flushingErrorObj = errorObject ; } public synchronized void setFlushingDone(){ setFlushingFailed(0,null); } @Override public synchronized StorageClassFlushInfo getStorageClassFlushInfo(){ return _flushInfo ; } } private class HFCPool implements HsmFlushControlCore.Pool { private final String _poolName; private HashMap<String, HFCFlushInfo> flushInfos = new HashMap<>() ; private PoolCellInfo cellInfo; private int mode; private boolean isActive; private long lastUpdated; private int answerCount; private HsmFlushControlCore.DriverHandle _driverHandle; private HFCPool( String poolName ){ _poolName = poolName ; } @Override public void setDriverHandle( HsmFlushControlCore.DriverHandle handle ){ _driverHandle = handle ; } @Override public String getName(){ return _poolName ; } public HsmFlushControlCore.PoolDetails getDetails(){ HsmFlushControllerPoolDetails details = new HsmFlushControllerPoolDetails() ; details._name = _poolName ; details._isActive = isActive ; details._isReadOnly = isReadOnly() ; details._cellInfo = cellInfo ; details._flushInfos = new ArrayList<>() ; for (HFCFlushInfo info : flushInfos.values()) { details._flushInfos.add(info.getDetails()); } return details ; } @Override public HsmFlushControlCore.DriverHandle getDriverHandle(){ return _driverHandle ; } @Override public boolean isActive(){ return isActive ; } @Override public void setReadOnly( boolean rdOnly ){ setPoolReadOnly( _poolName , rdOnly ) ; } @Override public void queryMode(){ queryPoolMode(_poolName ) ; } @Override public boolean isReadOnly(){ return ( mode & PoolManagerPoolModeMessage.WRITE ) == 0 ; } @Override public boolean isPoolIoModeKnown(){ return mode != 0 ; } @Override public Set<String> getStorageClassNames(){ return new TreeSet<>( flushInfos.keySet() ) ; } @Override public List<HsmFlushControlCore.FlushInfoDetails> getFlushInfos(){ return new ArrayList<>( flushInfos.values() ) ; } @Override public PoolCellInfo getCellInfo(){ return cellInfo ; } @Override public HsmFlushControlCore.FlushInfo getFlushInfoByStorageClass( String storageClass ){ return flushInfos.get( storageClass ); } public String getPoolModeString(){ if( mode == 0 ) { return "UU"; } String res; res = ( mode & PoolManagerPoolModeMessage.READ ) == 0 ? "-" : "R" ; res += ( mode & PoolManagerPoolModeMessage.WRITE ) == 0 ? "-" : "W" ; return res ; } public String toString(){ return _poolName + ";IOMode=" + getPoolModeString() + ";A=" + isActive + ";LU=" + (lastUpdated == 0 ? "Never" : (String.valueOf((System.currentTimeMillis() - lastUpdated) / 1000L))); } } private void flushStorageClass( String poolName , String storageClass , int count ) { PoolFlushDoFlushMessage msg = new PoolFlushDoFlushMessage( poolName , storageClass ) ; msg.setMaxFlushCount( count ) ; sendMessage( new CellMessage( new CellPath(poolName) , msg ) ) ; } private void setPoolReadOnly( String poolName , boolean rdOnly ){ try{ PoolManagerPoolModeMessage msg = new PoolManagerPoolModeMessage( poolName, PoolManagerPoolModeMessage.READ | ( rdOnly ? 0 : PoolManagerPoolModeMessage.WRITE ) ) ; sendMessage( new CellMessage( new CellAddressCore("PoolManager") , msg ) ) ; }catch(RuntimeException ee ){ _log.warn("setPoolReadOnly : couldn't sent message to PoolManager"+ee); } } private void queryPoolMode( String poolName ){ try{ PoolManagerPoolModeMessage msg = new PoolManagerPoolModeMessage( poolName, PoolManagerPoolModeMessage.UNDEFINED ) ; sendMessage( new CellMessage( new CellAddressCore("PoolManager") , msg ) ) ; }catch(RuntimeException ee ){ _log.warn("queryPoolMode : couldn't sent message to PoolManager"+ee); } } private class PoolCollector implements FutureCallback<Object[]> { private boolean _active; private int _waitingFor; private final HashMap<String, Object[]> _poolGroupHash = new HashMap<>() ; private HashMap<String, HFCPool> _configuredPoolList = new HashMap<>() ; private boolean _poolSetupReady; private PoolCollector(){ } private void queryAllPoolModes(){ List<HFCPool> list ; synchronized(this){ list = new ArrayList<>( _configuredPoolList.values() ) ; } for (HFCPool pool : list) { pool.queryMode(); } } private synchronized boolean isPoolSetupReady(){ return _poolSetupReady ; } private boolean isPoolConfigDone(){ int pools = 0 ; int c = 0 ; for( Iterator<HFCPool> i = _configuredPoolList.values().iterator() ; i.hasNext() ; pools++ ){ HFCPool pool = i.next(); if( pool.answerCount > 1 ) { return true; } if( pool.answerCount > 0 ) { c++; } } return c == pools ; } private synchronized Map<String, Object[]> getPoolGroupHash(){ return new HashMap<>( _poolGroupHash ) ; } private synchronized Set<String> getConfiguredPoolNames(){ return new HashSet<>( _configuredPoolList.keySet() ) ; } private synchronized List<HsmFlushControlCore.Pool> getConfiguredPools(){ return new ArrayList<>( _configuredPoolList.values() ) ; } private synchronized void runCollector( Collection<String> list ){ if( _active ){ _log.warn("runCollector : Still running") ; return ; } _active = true ; CellPath path = new CellPath( "PoolManager" ) ; for (Object o : list) { String command = "psux ls pgroup " + o; if (LOG_ENABLED) { _log.info("runCollector sending : " + command + " to " + path); } Futures.addCallback(_poolManager.send(command, Object[].class), this); _waitingFor++; } } private synchronized void answer(){ _waitingFor-- ; if( _waitingFor == 0 ){ if(LOG_ENABLED) { _log.info("PoolCollector : we are done : " + _poolGroupHash); } _active = false ; HashMap<String, HFCPool> map = new HashMap<>() ; if(LOG_ENABLED) { _log.info("Creating ping set"); } for (Object o : _poolGroupHash.values()) { Object[] c = (Object[]) o; for (Object aC : c) { String pool = (String) aC; if (LOG_ENABLED) { _log.info("Adding pool : " + pool); } HFCPool p = _configuredPoolList.get(pool); map.put(pool, p == null ? new HFCPool(pool) : p); } } // // get the diff // for (Object o : map.keySet()) { String poolName = o.toString(); if (_configuredPoolList.get(poolName) == null) { _eventDispatcher.configuredPoolAdded(poolName); } } for (Object o : _configuredPoolList.keySet()) { String poolName = o.toString(); if (map.get(poolName) == null) { _eventDispatcher.configuredPoolRemoved(poolName); } } _flushController.updatePoolList( map.keySet() ) ; _configuredPoolList = map ; _poolSetupReady = true ; _eventDispatcher.poolSetupReady() ; // _queueWatch.triggerGainControl() ; } } public synchronized HFCPool getPoolByName( String poolName ){ return _configuredPoolList.get(poolName); } @Override public void onSuccess(Object[] reply) { if(LOG_ENABLED) { _log.info("answer Arrived: {}", Arrays.toString(reply)); } if (reply.length >= 3 && reply[0] instanceof String && reply[1] instanceof Object []) { String poolGroupName = (String) reply[0] ; _poolGroupHash.put( poolGroupName , (Object[]) reply[1]); if(LOG_ENABLED) { _log.info("PoolCollector : " + ((Object[]) reply[1]).length + " pools arrived for " + poolGroupName); } }else{ _log.warn("PoolCollector : invalid reply arrived"); } answer(); } @Override public void onFailure(Throwable t) { _log.warn("PoolCollector : onFailure : {}", t.toString()); answer() ; } } public static final String hh_pgroup_add = "<pGroup0> [<pgroup1> [...]]" ; public String ac_pgroup_add_$_1_99( Args args ){ for( int i = 0 ; i < args.argc() ; i++ ){ _poolGroupList.add(args.argv(i)) ; } return "" ; } public static final String hh_pgroup_remove = "<pGroup0> [<pgroup1> [...]]" ; public String ac_pgroup_remove_$_1_99( Args args ){ for( int i = 0 ; i < args.argc() ; i++ ){ _poolGroupList.remove(args.argv(i)) ; } return "" ; } public static final String hh_pgroup_ls = "" ; public String ac_pgroup_ls( Args args ){ StringBuilder sb = new StringBuilder() ; for (Object poolGroup : _poolGroupList) { sb.append(poolGroup.toString()).append("\n"); } return sb.toString() ; } public static final String hh_set_control = "on|off [-interval=<seconds>]" ; public String ac_set_control_$_1( Args args ){ String mode = args.argv(0) ; String iString = args.getOpt("interval") ; long interval = 0L ; if( iString != null ){ interval = Long.parseLong( iString ) * 1000L ; if( interval < 30000L ) { throw new IllegalArgumentException("interval must be greater than 30"); } } switch (mode) { case "on": _flushController.setControl(true); break; case "off": _flushController.setControl(false); break; default: throw new IllegalArgumentException("set control on|off"); } if( interval > 0L ){ _queueWatch.setGainControlTicker( interval ) ; } return "" ; } public static final String hh_set_pool = "<poolName> rdonly|rw" ; public String ac_set_pool_$_2( Args args ) { String poolName = args.argv(0) ; String mode = args.argv(1) ; HFCPool pool = _poolCollector.getPoolByName( poolName ) ; if( pool == null ) { throw new NoSuchElementException("Pool not found : " + poolName); } switch (mode) { case "rdonly": pool.setReadOnly(true); break; case "rw": pool.setReadOnly(false); break; default: throw new IllegalArgumentException("Illegal mode : rdonly|rw"); } return "Pool "+poolName+" set to "+mode ; } public static final String hh_query_pool_mode = "<poolName>" ; public String ac_query_pool_mode_$_1( Args args ) { String poolName = args.argv(0) ; HFCPool pool = _poolCollector.getPoolByName( poolName ) ; if( pool == null ) { throw new NoSuchElementException("Pool not found : " + poolName); } pool.queryMode(); return "Pool mode query sent to Pool "+poolName ; } public static final String hh_flush_pool = "<poolName> <storageClass> [-count=<count>]" ; public String ac_flush_pool_$_2( Args args ) throws Exception { String poolName = args.argv(0) ; String storageClass = args.argv(1) ; String countString = args.getOpt("count"); int count = ( countString == null ) || countString.isEmpty() ? 0 : Integer.parseInt(countString) ; HFCPool pool = _poolCollector.getPoolByName( poolName ) ; if( pool == null ) { throw new NoSuchElementException("Pool not found : " + poolName); } HsmFlushControlCore.FlushInfo info = pool.getFlushInfoByStorageClass( storageClass ) ; if( info == null ) { throw new NoSuchElementException("StorageClass not found : " + storageClass); } info.flush( count ) ; return "Flush initiated for ("+poolName+","+storageClass+")" ; } public static final String hh_ls_pool = "[<poolName>] -l " ; public Object ac_ls_pool_$_0_1( Args args ){ String poolName = args.argc() == 0 ? null : args.argv(0) ; boolean detail = args.hasOption("l") ; boolean binary = args.hasOption("binary") ; StringBuffer sb = new StringBuffer() ; if( poolName == null ){ if( binary ){ Collection<HsmFlushControlCore.PoolDetails> list = new ArrayList<>() ; for (Object pool : _eventDispatcher.getConfiguredPools()) { list.add(((HFCPool) pool).getDetails()); } return list ; }else{ Set<String> set = new TreeSet<>( _poolCollector.getConfiguredPoolNames() ) ; for (Object configuredPoolName : set) { String name = configuredPoolName.toString(); if (!detail) { sb.append(name).append("\n"); } else { HFCPool pool = _poolCollector.getPoolByName(name); printPoolDetails2(pool, sb); } } } }else{ HFCPool pool = _poolCollector.getPoolByName( poolName ) ; if( pool == null ) { throw new NoSuchElementException("Pool not found : " + poolName); } if( binary ){ return pool.getDetails() ; /* HsmFlushControlCore.PoolDetails details = pool.getDetails() ; sb.append("PoolDetails\n"). append(" Name ").append(details.getName()).append("\n"). append(" CellInfo ").append(details.getCellInfo()).append("\n"). append(" isActive ").append(details.isActive()).append("\n") ; for( Iterator i = details.getFlushInfos().iterator() ; i.hasNext() ; ){ HsmFlushControlCore.FlushInfoDetails flush = (HsmFlushControlCore.FlushInfoDetails)i.next() ; sb.append(" StorageClass ").append(flush.getName()).append("\n"). append(" isFlushing ").append(flush.isFlushing()).append("\n"). append(" FlushInfo ").append(flush.getStorageClassFlushInfo()).append("\n"); } */ }else{ printPoolDetails2( pool , sb ) ; } } return sb.toString() ; } private void printPoolDetails2( HFCPool pool , StringBuffer sb ){ sb.append( pool._poolName ) ; sb.append("\n").append(pool.toString()).append(" "); if( pool.cellInfo == null ) { return; } PoolCellInfo cellInfo = pool.cellInfo ; PoolCostInfo costInfo = cellInfo.getPoolCostInfo() ; PoolCostInfo.PoolSpaceInfo spaceInfo = costInfo.getSpaceInfo() ; PoolCostInfo.PoolQueueInfo queueInfo = costInfo.getStoreQueue() ; long total = spaceInfo.getTotalSpace() ; long precious = spaceInfo.getPreciousSpace() ; sb.append(" ").append("Mode=").append(pool.getPoolModeString()). append(";Total=").append(total). append(";Precious=").append(precious). append(";Frac=").append( (float)precious/(float)total ). append(";Queue={").append(queueInfo.toString()).append("}\n"); for (Object o : pool.flushInfos.values()) { HFCFlushInfo info = (HFCFlushInfo) o; StorageClassFlushInfo flush = info.getStorageClassFlushInfo(); String storeName = flush.getStorageClass() + "@" + flush.getHsm(); long size = flush.getTotalPendingFileSize(); int count = flush.getRequestCount(); int active = flush.getActiveCount(); sb.append(" ").append(storeName).append(" "). append(info._flushingRequested ? "R" : "-"). append(info._flushingPending ? "P" : "-"). append(info._flushingError != 0 ? "E" : "-"). append("(").append(info._flushingId).append(")"). append(";Size=").append(size). append(";Count=").append(count). append(";Active=").append(active).append("\n"); } } private void printFlushInfo( StorageClassFlushInfo flush , StringBuffer sb ){ sb.append("count=").append(flush.getRequestCount()). append(";bytes=").append(flush.getTotalPendingFileSize()); } /* public static final String hh_infos_ls = "" ; public String ac_infos_ls( Args args ){ HashMap map = new HashMap( _infoHash ) ; StringBuffer sb = new StringBuffer() ; for( Iterator it = map.entrySet().iterator() ; it.hasNext() ; ){ Map.Entry entry = (Map.Entry)it.next() ; String poolName = entry.getKey().toString() ; PoolFlushGainControlMessage msg = (PoolFlushGainControlMessage)entry.getValue() ; long holdTimer = msg.getHoldTimer() ; PoolCellInfo poolInfo = msg.getCellInfo() ; StorageClassFlushInfo [] flush = msg.getFlushInfos() ; sb.append( poolName ).append("\n"); sb.append( " ").append("Hold Timer : ").append(holdTimer).append("\n"); sb.append( " ").append("CellInfo : ").append(poolInfo.toString()).append("\n"); if( flush != null ){ for( int j = 0 ; j < flush.length ; j++ ){ sb.append(" Flush(").append(j).append(") ").append(flush[j]).append("\n"); } } } return sb.toString(); } */ public String toString(){ return "Req="+_requests+";Err="+_failed+";" ; } @Override public void getInfo( PrintWriter pw ){ pw.println("HsmFlushControlManager : [$Id: HsmFlushControlManager.java,v 1.6 2006-07-31 16:35:50 patrick Exp $]" ) ; pw.println("Status : "+_status); pw.print("PoolGroups : "); for (Map.Entry<String,Object[]> entry : _poolCollector.getPoolGroupHash().entrySet()) { String groupName = entry.getKey(); Object[] pools = entry.getValue(); pw.println(groupName + "(" + pools.length + "),"); } pw.println(""); pw.println("Driver : "+(_eventDispatcher._schedulerName==null?"NONE":_eventDispatcher._schedulerName)); pw.println("Control : "+( _flushController._control ? "on" : "off" ) ) ; pw.println("Update : "+( _queueWatch._gainControlTicker/1000L )+ " seconds"); long propertiesUpdated = _propertiesUpdated ; if( propertiesUpdated == 0L ){ pw.println("Update : "+( _queueWatch._gainControlTicker/1000L )+ " seconds"); pw.println("Properties : Not queried yet"); }else{ Map<String,Object> properties; synchronized( _propertyLock ){ properties = _properties ; } if( ( properties != null ) && (!properties.isEmpty()) ){ pw.println("Properties : (age "+( ( System.currentTimeMillis() - propertiesUpdated ) / 1000L )+" seconds)" ); for (Map.Entry<String,Object> entry : properties.entrySet()) { pw.println(" " + entry.getKey() + "=" + entry.getValue()); } }else{ pw.println("Properties : None"); } } } @Override public CellInfo getCellInfo(){ FlushControlCellInfo info = new FlushControlCellInfo( super.getCellInfo() ) ; info.setParameter( _eventDispatcher._schedulerName==null?"NONE":_eventDispatcher._schedulerName , _queueWatch._gainControlTicker , _flushController._control , new ArrayList<>( _poolCollector.getPoolGroupHash().keySet() ) , _status ) ; synchronized( _propertyLock ){ info.setDriverProperties( System.currentTimeMillis() - _propertiesUpdated , _properties ) ; } //_log.info("DRIVER PROPERTIES : "+info ) ; return info ; } private class EventDispatcher implements HsmFlushControlCore, Runnable { private class Event { private static final int INIT = 0 ; private static final int UNLOAD = 1 ; private static final int FLUSHING_DONE = 2 ; private static final int POOL_FLUSH_INFO_UPDATED = 3 ; private static final int CALL_DRIVER = 4 ; private static final int POOL_SETUP_UPDATED = 5 ; private static final int CONFIGURED_POOL_ADDED = 6 ; private static final int CONFIGURED_POOL_REMOVED = 7 ; private static final int POOL_IO_MODE_UPDATED = 8 ; private static final int TIMER = 9 ; private static final int PROPERTIES_UPDATED = 10 ; private static final int RESET = 11 ; final int type; Object [] args; long timestamp = System.currentTimeMillis() ; private Event( int type ){ this.type = type ; } private Event( int type , Object obj ){ this.type = type ; args = new Object[1] ; args[0] = obj ; } public String toString(){ StringBuilder sb = new StringBuilder() ; sb.append("Event type : ").append(type); if( args != null ){ for( int i = 0 ; i < args.length;i++) { sb.append(";arg(").append(i).append(")=") .append(args[i].toString()); } } return sb.toString(); } } private class Pipe { private final ArrayList<Event> _list = new ArrayList<>() ; public synchronized void push( Event obj ){ _list.add( obj ) ; notifyAll() ; } public synchronized Event pop() throws InterruptedException { while( _list.isEmpty() ) { wait(); } return _list.remove(0); } } private final Class<?>[] _argumentClasses = { CellAdapter.class , HsmFlushControlCore.class } ; private final Object[] _argumentObjects = { diskCacheV111.hsmControl.flush.HsmFlushControlManager.this , this } ; private HsmFlushSchedulable _scheduler; private String _schedulerName; private String _printoutName; private Pipe _pipe; private Thread _worker; private boolean _initDone; private Args _driverArgs = new Args("") ; @Override public void run(){ runIt( _pipe ) ; } public void runIt( Pipe pipe ) { HsmFlushSchedulable s; Event event; boolean done = false ; boolean initDone = false ; _log.info(_printoutName+": Worker event loop started for "+_schedulerName ); while( ( ! Thread.interrupted() ) && ( ! done ) ){ try{ event = pipe.pop() ; }catch(InterruptedException e ){ break ; } synchronized( this ){ s = _scheduler ; if( s == null ) { break; } } try{ if( ( ! initDone ) && _poolCollector.isPoolSetupReady() && _poolCollector.isPoolConfigDone() ){ s.init() ; initDone = true ; _poolCollector.queryAllPoolModes() ; } if( ! initDone ) { continue; } switch( event.type ){ case Event.FLUSHING_DONE : HFCFlushInfo info = (HFCFlushInfo)event.args[0] ; s.flushingDone( info._pool._poolName , info._flushInfo.getStorageClass()+"@"+ info._flushInfo.getHsm() , info ) ; break ; case Event.POOL_FLUSH_INFO_UPDATED : { HFCPool pool = (HFCPool)event.args[0] ; s.poolFlushInfoUpdated( pool._poolName , pool) ; } break ; case Event.POOL_IO_MODE_UPDATED : { HFCPool pool = (HFCPool)event.args[0] ; s.poolIoModeUpdated( pool._poolName , pool) ; } break ; case Event.CONFIGURED_POOL_ADDED : { String poolName = (String)event.args[0] ; s.configuredPoolAdded( poolName ) ; } break ; case Event.CONFIGURED_POOL_REMOVED : { String poolName = (String)event.args[0] ; s.configuredPoolRemoved( poolName ) ; } break ; case Event.PROPERTIES_UPDATED : { Map<String,Object> properties = (Map<String, Object>) event.args[0]; s.propertiesUpdated( properties ) ; synchronized( _propertyLock ){ _propertiesUpdated = System.currentTimeMillis() ; _properties = properties ; } } break ; case Event.POOL_SETUP_UPDATED : s.poolSetupUpdated() ; break ; case Event.CALL_DRIVER : Args args = (Args)event.args[0] ; s.command( args ) ; break ; case Event.TIMER : s.timer() ; break ; case Event.RESET : s.reset() ; break ; case Event.UNLOAD : done = true ; break ; } }catch(Throwable t ){ _log.warn(_printoutName+": Exception reported by "+event.type+" : "+t, t); } } _log.info(_printoutName+": Worker event loop stopped"); _log.info(_printoutName+": Preparing unload"); synchronized( this ){ if( _scheduler == null ) { return; } try{ _scheduler.prepareUnload() ; }catch(Throwable t ){ _log.warn(_printoutName+": Exception in prepareUnload "+t, t); } _scheduler = null ; _worker = null ; _initDone = false ; } } private synchronized void flushingDone( HFCFlushInfo info ){ if( _pipe != null ) { _pipe.push(new Event(Event.FLUSHING_DONE, info)); } } private synchronized void poolSetupReady(){ if( _pipe != null ) { _pipe.push(new Event(Event.POOL_SETUP_UPDATED, null)); } } /* private synchronized void poolSetupReady(){ if( ( _scheduler != null ) && ( _pipe != null ) && ! _initDone ) _pipe.push( new Event( Event.INIT ) ) ; } */ private synchronized void poolFlushInfoUpdated( HFCPool pool ){ if( _pipe != null ) { _pipe.push(new Event(Event.POOL_FLUSH_INFO_UPDATED, pool)); } } private synchronized void configuredPoolAdded( String pool ){ if( _pipe != null ) { _pipe.push(new Event(Event.CONFIGURED_POOL_ADDED, pool)); } } private synchronized void configuredPoolRemoved( String pool ){ if( _pipe != null ) { _pipe.push(new Event(Event.CONFIGURED_POOL_REMOVED, pool)); } } private synchronized void callDriver( Args args ){ if( _pipe != null ) { _pipe.push(new Event(Event.CALL_DRIVER, args)); } } private synchronized void poolIoModeUpdated( HFCPool pool ){ if( _pipe != null ) { _pipe.push(new Event(Event.POOL_IO_MODE_UPDATED, pool)); } } private synchronized void propertiesUpdated( Map<String, String> properties ){ if( _pipe != null ) { _pipe.push(new Event(Event.PROPERTIES_UPDATED, properties)); } } private synchronized void timer(){ if( _pipe != null ) { _pipe.push(new Event(Event.TIMER)); } } private synchronized void reset(){ if( _pipe != null ) { _pipe.push(new Event(Event.RESET)); } } private synchronized void loadHandler( String handlerName , boolean doInit , Args args ) throws Exception { if( _scheduler != null ) { throw new IllegalArgumentException("Handler already registered"); } Class<? extends HsmFlushSchedulable> c = Class.forName(handlerName).asSubclass(HsmFlushSchedulable.class); Constructor<? extends HsmFlushSchedulable> con = c.getConstructor(_argumentClasses); _driverArgs = args ; _schedulerName = handlerName ; String [] tmp = handlerName.split("\\.") ; _printoutName = tmp[ tmp.length - 1 ] ; _scheduler = con.newInstance( _argumentObjects ); _pipe = new Pipe() ; _worker = _nucleus.newThread( this , "driver" ) ; _worker.start() ; /* if( doInit ){ _initDone = true ; _pipe.push( new Event( Event.INIT ) ) ; } */ } private synchronized void unloadHandler(){ if( _pipe == null ) { throw new IllegalArgumentException("No handler active"); } Pipe pipe = _pipe ; _pipe = null ; pipe.push( new Event( Event.UNLOAD ) ) ; } // // interface HsmFlushControlCore ... // @Override public Args getDriverArgs(){ return _driverArgs ; } @Override public Pool getPoolByName( String poolName ){ return _poolCollector.getPoolByName( poolName) ; } @Override public List<Pool> getConfiguredPools(){ return _poolCollector.getConfiguredPools() ; } @Override public Set<String> getConfiguredPoolNames(){ return _poolCollector.getConfiguredPoolNames() ; } } public static final String hh_driver_reset = " # resets driver " ; public String ac_driver_reset( Args args ) { _eventDispatcher.callDriver( args ) ; return "Command sent to driver" ; } public static final String hh_driver_command = " commands send to driver ... " ; public String ac_driver_command_$_0_999( Args args ) { _eventDispatcher.callDriver( args ) ; return "Command sent to driver" ; } public static final String hh_driver_properties = " OPTIONS : -<key>=<value> ..." ; public String ac_driver_properties( Args args ) { Map<String, String> map = new HashMap<>() ; for( int i = 0 , n = args.optc() ; i < n ; i++ ){ String key = args.optv(i) ; String value = args.getOpt(key) ; map.put( key , value ) ; } _eventDispatcher.propertiesUpdated( map ) ; return "Properties sent to driver, check with 'info'" ; } public static final String hh_load_driver = "<driveClassName> [driver arguments and options]" ; public String ac_load_driver_$_999( Args args ) throws Exception { String driverClass = args.argv(0) ; args.shift(); _eventDispatcher.loadHandler( driverClass , true , args ) ; return "Loaded : "+driverClass; } public static final String hh_unload_driver = "" ; public String ac_unload_driver( Args args ) { _eventDispatcher.unloadHandler() ; return "Unload scheduled"; } ///////////////////////////////////////////////////////////////////////////////////////// // // message arrived handler // private void poolFlushDoFlushMessageArrived( PoolFlushDoFlushMessage msg ){ if(LOG_ENABLED) { _log.info("poolFlushDoFlushMessageArrived : " + msg); } String poolName = msg.getPoolName() ; synchronized( _poolCollector ){ HFCPool pool = _poolCollector.getPoolByName( poolName) ; if( pool == null ){ _log.warn("poolFlushDoFlushMessageArrived : message arrived for non configured pool : "+poolName); return ; } String storageClass = msg.getStorageClassName()+"@"+msg.getHsmName() ; HFCFlushInfo info = pool.flushInfos.get( storageClass ); if( info == null ){ _log.warn("poolFlushDoFlushMessageArrived : message arrived for non existing storage class : "+storageClass); // // the real one doesn't exists anymore, so we simulate one. // info = new HFCFlushInfo( pool , new StorageClassFlushInfo( msg.getHsmName() , msg.getStorageClassName() ) ) ; } if( msg.getReturnCode() != 0 ){ if(LOG_ENABLED) { _log.info("Flush failed (msg=" + (msg .isFinished() ? "Finished" : "Ack") + ") : " + msg); } info.setFlushingFailed(msg.getReturnCode(),msg.getErrorObject()); // // maybe we have to call flushingDone here as well. // return ; } if( msg.isFinished() ){ if(LOG_ENABLED) { _log.info("Flush finished : " + msg); } updateFlushCellAndFlushInfos( msg , pool ) ; info.setFlushingDone() ; _eventDispatcher.flushingDone(info); }else{ info.setFlushingAck(msg.getFlushId()); } } } private void poolFlushGainControlMessageDidntArrive( String poolName ){ if(LOG_ENABLED) { _log.info("poolFlushGainControlMessageDidntArrive : " + poolName); } synchronized( _poolCollector ){ HFCPool pool = _poolCollector.getPoolByName( poolName) ; if( pool == null ){ _log.warn("PoolFlushGainControlMessage : message arrived for non configured pool : "+poolName); return ; } pool.isActive = false ; pool.lastUpdated = System.currentTimeMillis(); pool.answerCount ++ ; _eventDispatcher.poolFlushInfoUpdated( pool ) ; } } private void updateFlushCellAndFlushInfos( PoolFlushControlInfoMessage msg , HFCPool pool ){ pool.cellInfo = msg.getCellInfo() ; StorageClassFlushInfo [] flushInfos = msg.getFlushInfos() ; HashMap<String, HFCFlushInfo> map = new HashMap<>() ; if( flushInfos != null ){ for (StorageClassFlushInfo flushInfo : flushInfos) { String storageClass = flushInfo.getStorageClass() + "@" + flushInfo .getHsm(); HFCFlushInfo info = pool.flushInfos .get(storageClass); if (info == null) { map.put(storageClass, new HFCFlushInfo(pool, flushInfo)); } else { info.updateFlushInfo(flushInfo); map.put(storageClass, info); } } } pool.flushInfos = map ; pool.isActive = true ; pool.lastUpdated = System.currentTimeMillis(); pool.answerCount ++ ; } private void poolFlushGainControlMessageArrived( PoolFlushGainControlMessage msg ){ if(LOG_ENABLED) { _log.info("PoolFlushGainControlMessage : " + msg); } String poolName = msg.getPoolName() ; synchronized( _poolCollector ){ HFCPool pool = _poolCollector.getPoolByName( poolName) ; if( pool == null ){ _log.warn("PoolFlushGainControlMessage : message arrived for non configured pool : "+poolName); return ; } updateFlushCellAndFlushInfos( msg , pool ) ; _eventDispatcher.poolFlushInfoUpdated( pool ) ; } } private void poolModeInfoArrived( PoolManagerPoolModeMessage msg ){ if(LOG_ENABLED) { _log.info("PoolManagerPoolModeMessage : " + msg); } String poolName = msg.getPoolName() ; synchronized( _poolCollector ){ HFCPool pool = _poolCollector.getPoolByName( poolName) ; if( pool == null ){ _log.warn("poolModeInfoArrived : message arrived for non configured pool : "+poolName); return ; } pool.mode = msg.getPoolMode() ; _eventDispatcher.poolIoModeUpdated( pool ) ; } } private void poolStatusChanged( PoolStatusChangedMessage msg ){ String poolName = msg.getPoolName() ; } @Override public void messageArrived( CellMessage msg ){ Object obj = msg.getMessageObject() ; _requests ++ ; if( obj instanceof PoolFlushGainControlMessage ){ poolFlushGainControlMessageArrived( (PoolFlushGainControlMessage) obj ) ; }else if( obj instanceof PoolFlushDoFlushMessage ){ poolFlushDoFlushMessageArrived( (PoolFlushDoFlushMessage) obj ) ; }else if( obj instanceof PoolManagerPoolModeMessage ){ poolModeInfoArrived( (PoolManagerPoolModeMessage) obj ) ; }else if( obj instanceof PoolStatusChangedMessage ){ poolStatusChanged( (PoolStatusChangedMessage) obj ) ; }else if( obj instanceof NoRouteToCellException ){ NoRouteToCellException nrtc = (NoRouteToCellException)obj ; CellPath path = nrtc.getDestinationPath() ; CellAddressCore core = path.getDestinationAddress() ; String cellName = core.getCellName() ; _log.warn( "NoRouteToCell : "+ cellName + " ("+path+")"); poolFlushGainControlMessageDidntArrive( cellName ) ; }else{ _log.warn("Unknown message arrived ("+msg.getSourcePath()+") : "+ msg.getMessageObject() ) ; _failed ++ ; } } }