package diskCacheV111.services.space; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import org.springframework.dao.DataAccessException; import org.springframework.dao.DeadlockLoserDataAccessException; import org.springframework.dao.RecoverableDataAccessException; import org.springframework.dao.TransientDataAccessException; import org.springframework.remoting.RemoteAccessException; import org.springframework.transaction.CannotCreateTransactionException; import org.springframework.transaction.TransactionException; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.text.ParseException; import java.util.Collection; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import diskCacheV111.util.CacheException; import diskCacheV111.util.VOInfo; import dmg.cells.nucleus.CellAddressCore; import dmg.cells.nucleus.CellCommandListener; import dmg.cells.nucleus.CellIdentityAware; import dmg.cells.nucleus.CellInfoProvider; import dmg.cells.nucleus.CellLifeCycleAware; import dmg.cells.nucleus.NoRouteToCellException; import dmg.util.command.Command; import dmg.util.command.DelayedCommand; import org.dcache.poolmanager.PoolLinkGroupInfo; import org.dcache.poolmanager.RemotePoolMonitor; import org.dcache.poolmanager.Utils; public class LinkGroupLoader implements CellCommandListener, CellLifeCycleAware, CellInfoProvider, CellIdentityAware, Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(LinkGroupLoader.class); private static final long EAGER_LINKGROUP_UPDATE_PERIOD = 1000; private long updateLinkGroupsPeriod; private File authorizationFileName; private long latestUpdateTime = System.currentTimeMillis(); private LinkGroupAuthorizationFile linkGroupAuthorizationFile; private long authorizationFileLastUpdateTimestamp; private RemotePoolMonitor poolMonitor; private SpaceManagerDatabase db; private ScheduledExecutorService executor; private CellAddressCore cellAddress; @Override public void setCellAddress(CellAddressCore address) { cellAddress = address; } @Required public void setUpdateLinkGroupsPeriod(long updateLinkGroupsPeriod) { this.updateLinkGroupsPeriod = updateLinkGroupsPeriod; } @Required public void setDatabase(SpaceManagerDatabase db) { this.db = db; } @Required public void setPoolMonitor(RemotePoolMonitor poolMonitor) { this.poolMonitor = poolMonitor; } @Required public void setAuthorizationFileName(File authorizationFileName) { this.authorizationFileName = authorizationFileName; } public long getLatestUpdateTime() { return latestUpdateTime; } public void start() { executor = Executors.newSingleThreadScheduledExecutor(); } @Override public void afterStart() { executor.schedule(this, 0, TimeUnit.MILLISECONDS); } public void stop() { if (executor != null) { executor.shutdownNow(); } } @Override public void getInfo(PrintWriter printWriter) { printWriter.append("updateLinkGroupsPeriod = ").println(updateLinkGroupsPeriod); printWriter.append("authorizationFileName = ").println(authorizationFileName); } @Override public void run() { long period = EAGER_LINKGROUP_UPDATE_PERIOD; try { if (updateLinkGroups() > 0) { period = updateLinkGroupsPeriod; } } catch (RemoteAccessException | DataAccessException | TransactionException e) { LOGGER.error("Link group update failed: {}", e.getMessage()); } catch (RuntimeException e) { LOGGER.error("Link group update failed: " + e.toString(), e); } catch (InterruptedException e) { LOGGER.trace("update LinkGroup thread has been interrupted"); } finally { executor.schedule(this, period, TimeUnit.MILLISECONDS); } } private void loadLinkGroupAuthorizationFile() { File file = authorizationFileName; if(file == null) { return; } if(!file.exists()) { linkGroupAuthorizationFile = null; } long lastModified = file.lastModified(); if (linkGroupAuthorizationFile == null|| lastModified >= authorizationFileLastUpdateTimestamp) { authorizationFileLastUpdateTimestamp = lastModified; try { linkGroupAuthorizationFile = new LinkGroupAuthorizationFile(file); } catch (IOException | ParseException e) { LOGGER.error("Failed to read {}: {}", file, e.toString()); } } } private int updateLinkGroups() throws InterruptedException, RemoteAccessException, DataAccessException, TransactionException { long currentTime = System.currentTimeMillis(); Collection<PoolLinkGroupInfo> linkGroupInfos = Utils.linkGroupInfos(poolMonitor.getPoolSelectionUnit(), poolMonitor.getCostModule()).values(); if (!linkGroupInfos.isEmpty()) { loadLinkGroupAuthorizationFile(); for (PoolLinkGroupInfo info : linkGroupInfos) { saveLinkGroup(currentTime, info); } } latestUpdateTime = currentTime; return linkGroupInfos.size(); } private void saveLinkGroup(long currentTime, PoolLinkGroupInfo info) throws InterruptedException { String linkGroupName = info.getName(); long avalSpaceInBytes = info.getAvailableSpaceInBytes(); VOInfo[] vos = null; boolean onlineAllowed = info.isOnlineAllowed(); boolean nearlineAllowed = info.isNearlineAllowed(); boolean replicaAllowed = info.isReplicaAllowed(); boolean outputAllowed = info.isOutputAllowed(); boolean custodialAllowed = info.isCustodialAllowed(); if (linkGroupAuthorizationFile != null) { LinkGroupAuthorizationRecord record = linkGroupAuthorizationFile .getLinkGroupAuthorizationRecord(linkGroupName); if (record != null) { vos = record.getVOInfoArray(); } } while (true) { try { db.updateLinkGroup(linkGroupName, avalSpaceInBytes, currentTime, onlineAllowed, nearlineAllowed, replicaAllowed, outputAllowed, custodialAllowed, vos); break; } catch (DeadlockLoserDataAccessException e) { LOGGER.info("Update of link group {} lost deadlock race and will be retried: {}", linkGroupName, e.toString()); } catch (TransientDataAccessException | RecoverableDataAccessException | CannotCreateTransactionException e) { LOGGER.warn("Update of link group {} failed and will be retried: {}", linkGroupName, e.getMessage()); } Thread.sleep(500); } } @Command(name = "update link groups", hint = "update link group information", description = "Link groups are periodically imported from pool manager and stored in " + "the space manager database. This command performs an immediate " + "update of the link group information.") public class UpdateLinkGroupsCommand extends DelayedCommand<String> { public UpdateLinkGroupsCommand() { super(executor); } @Override protected String execute() throws InterruptedException, DataAccessException, TransactionException, CacheException, NoRouteToCellException { poolMonitor.refresh(); int updated = updateLinkGroups(); return updated + (updated == 1 ? " link group " : " link groups ") + "updated."; } } }