package hudson.plugins.disk_usage; import hudson.model.*; import hudson.Extension; import hudson.FilePath; import hudson.init.InitMilestone; import hudson.init.Initializer; import java.io.File; import java.io.IOException; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; /** * This Property sets DiskUsage action. * * @author dvrzalik */ public class DiskUsageProperty extends JobProperty<Job<?, ?>> { @Override public Collection<? extends Action> getJobActions(Job<?, ?> job) { return Collections.emptyList(); } private transient ProjectDiskUsage diskUsage = new ProjectDiskUsage(); @Deprecated private Long diskUsageWithoutBuilds; @Deprecated private Map<String,Map<String,Long>> slaveWorkspacesUsage; public void setDiskUsageWithoutBuilds(Long diskUsageWithoutBuilds){ if(diskUsage==null){ diskUsage = new ProjectDiskUsage(); } diskUsage.load(); diskUsage.setDiskUsageWithoutBuilds(diskUsageWithoutBuilds); saveDiskUsage(); } public void remove(Node node, String path){ Map<String,Long> workspacesInfo = getSlaveWorkspaceUsage().get(node.getNodeName()); workspacesInfo.remove(path); if(workspacesInfo.isEmpty()){ getSlaveWorkspaceUsage().remove(node.getNodeName()); } saveDiskUsage(); } public Set<DiskUsageBuildInformation> getDiskUsageOfBuilds(){ return diskUsage.getBuildDiskUsage(false); } public Long getDiskUsageOfBuild(String buildId){ for(DiskUsageBuildInformation information: diskUsage.getBuildDiskUsage(false)){ if(buildId.equals(information.getId())){ return information.getSize(); } } return 0L; } public DiskUsageBuildInformation getDiskUsageBuildInformation(String buildId){ for(DiskUsageBuildInformation information: diskUsage.getBuildDiskUsage(false)){ if(buildId.equals(information.getId())){ return information; } } return null; } public Long getAllDiskUsageOfBuild(String buildId){ if(getDiskUsageBuildInformation(buildId) == null){ return 0L; } return getAllDiskUsageOfBuild(getDiskUsageBuildInformation(buildId).getNumber()); } public Long getAllDiskUsageOfBuild(int buildNumber){ Long size = getDiskUsageOfBuild(buildNumber); if(owner instanceof ItemGroup){ ItemGroup group = (ItemGroup) owner; for(Object item : group.getItems()){ if(item instanceof AbstractProject){ AbstractProject project = (AbstractProject) item; DiskUsageProperty property = (DiskUsageProperty) project.getProperty(DiskUsageProperty.class); size += property.getAllDiskUsageOfBuild(buildNumber); } } } return size; } public DiskUsageBuildInformation getDiskUsageBuildInformation(int buildNumber){ for(DiskUsageBuildInformation information: diskUsage.getBuildDiskUsage(false)){ if(buildNumber == information.getNumber()){ return information; } } return null; } public Long getDiskUsageOfBuild(int buildNumber){ for(DiskUsageBuildInformation information: diskUsage.getBuildDiskUsage(false)){ if(buildNumber == information.getNumber()){ return information.getSize(); } } return 0L; } public ProjectDiskUsage getProjectDiskUsage(){ return diskUsage; } public void setDiskUsage(ProjectDiskUsage usage){ diskUsage = usage; } public ProjectDiskUsage getDiskUsage(){ return diskUsage; } public Long getDiskUsageWithoutBuildDirectory(){ return diskUsage.getDiskUsageWithoutBuildDirectory(); } @Override public void setOwner(Job job){ super.setOwner(job); if(diskUsage==null){ diskUsage = new ProjectDiskUsage(); } diskUsage.setProject(job); loadDiskUsage(); //transfer old data boolean modified = false; if(diskUsageWithoutBuilds!=null){ diskUsage.setDiskUsageWithoutBuilds(diskUsageWithoutBuilds); diskUsageWithoutBuilds = null; modified = true; } if(slaveWorkspacesUsage!=null){ diskUsage.slaveWorkspacesUsage.putAll(slaveWorkspacesUsage); slaveWorkspacesUsage = null; modified = true; } if(modified){ saveDiskUsage(); try{ job.save(); } catch(Exception e){ Logger.getLogger(getClass().getName()).log(Level.WARNING, "configuration of project " + job.getDisplayName() + " can not be saved.", e); } } } public void putSlaveWorkspace(Node node, String path){ Map<String,Long> workspacesInfo = getSlaveWorkspaceUsage().get(node.getNodeName()); if(workspacesInfo==null){ workspacesInfo = new ConcurrentHashMap<String,Long>(); } if(!workspacesInfo.containsKey(path)) workspacesInfo.put(path, 0l); getSlaveWorkspaceUsage().put(node.getNodeName(), workspacesInfo); saveDiskUsage(); } public Map<String,Map<String,Long>> getSlaveWorkspaceUsage(){ if(diskUsage.slaveWorkspacesUsage==null){ // diskUsage.slaveWorkspacesUsage = new ConcurrentHashMap<String,Map<String,Long>>(); checkWorkspaces(); } return diskUsage.slaveWorkspacesUsage; } public void putSlaveWorkspaceSize(Node node, String path, Long size){ Map<String,Long> workspacesInfo = getSlaveWorkspaceUsage().get(node.getNodeName()); if(workspacesInfo==null) workspacesInfo = new ConcurrentHashMap<String,Long>(); workspacesInfo.put(path, size); getSlaveWorkspaceUsage().put(node.getNodeName(), workspacesInfo); saveDiskUsage(); } public Long getWorkspaceSize(Boolean containdedInWorkspace){ Long size = 0l; for(String nodeName: getSlaveWorkspaceUsage().keySet()){ Node node = Jenkins.getInstance().getNode(nodeName); String workspacePath = null; if(node instanceof Jenkins){ workspacePath = Jenkins.getInstance().getRawWorkspaceDir(); } if(node instanceof Slave){ workspacePath = ((Slave) node).getRemoteFS(); } if(workspacePath==null) continue; Map<String,Long> paths = getSlaveWorkspaceUsage().get(nodeName); for(String path: paths.keySet()){ if(containdedInWorkspace.equals(path.startsWith(workspacePath))){ size += paths.get(path); } } } return size; } private void checkAllBuilds(){ List<AbstractBuild> builds = (List<AbstractBuild>) owner.getBuilds(); for(AbstractBuild build: builds){ if(!build.isBuilding()){ Node node = build.getBuiltOn(); FilePath path = build.getWorkspace(); if(path==null) continue; Map<String,Long> workspacesInfo = diskUsage.slaveWorkspacesUsage.get(node.getNodeName()); if(workspacesInfo==null){ workspacesInfo = new ConcurrentHashMap<String,Long>(); workspacesInfo.put(path.getRemote(), 0L); } else{ if(!workspacesInfo.keySet().contains(path.getRemote())) workspacesInfo.put(path.getRemote(), 0L); } getSlaveWorkspaceUsage().put(node.getNodeName(), workspacesInfo); } } } private void checkLoadedBuilds(){ if(owner instanceof AbstractProject){ AbstractProject project = (AbstractProject) owner; Collection<AbstractBuild> builds = project._getRuns().getLoadedBuilds().values(); for(AbstractBuild build: builds){ if(!build.isBuilding()){ Node node = build.getBuiltOn(); FilePath path = build.getWorkspace(); if(path==null) continue; Map<String,Long> workspacesInfo = diskUsage.slaveWorkspacesUsage.get(node.getNodeName()); if(workspacesInfo==null){ workspacesInfo = new ConcurrentHashMap<String,Long>(); workspacesInfo.put(path.getRemote(), 0L); } else{ if(!workspacesInfo.keySet().contains(path.getRemote())) workspacesInfo.put(path.getRemote(), 0L); } getSlaveWorkspaceUsage().put(node.getNodeName(), workspacesInfo); } } } } public void checkWorkspaces(){ checkWorkspaces(false); } public void checkWorkspaces(boolean force) { if(force){ checkAllBuilds(); } else{ checkLoadedBuilds(); } //only if it is wanted - can cost a quite long time to do it for all if(Jenkins.getInstance().getPlugin(DiskUsagePlugin.class).getConfiguration().getCheckWorkspaceOnSlave() && owner instanceof TopLevelItem){ for(Node node: Jenkins.getInstance().getNodes()){ if(node.toComputer()!=null && node.toComputer().isOnline()){ FilePath path =null; try{ path = node.getWorkspaceFor((TopLevelItem)owner); if(path!=null && path.exists() && (diskUsage.slaveWorkspacesUsage.get(node.getNodeName())==null || !diskUsage.slaveWorkspacesUsage.get(node.getNodeName()).containsKey(path.getRemote()))){ putSlaveWorkspace(node, path.getRemote()); } } catch(Exception e){ LOGGER.warning("Can not check if file " + path.getRemote() + " exists on node " + node.getNodeName()); } } } } Iterator<String> iterator = diskUsage.slaveWorkspacesUsage.keySet().iterator(); while(iterator.hasNext()){ String nodeName = iterator.next(); Node node = Jenkins.getInstance().getNode(nodeName); if(node==null && nodeName.isEmpty()) node = Jenkins.getInstance(); //delete name of slaves which do not exist if(node ==null) {//Jenkins master has empty name iterator.remove(); } else{ //delete path which does not exists if(node!=null && node.toComputer()!=null && node.getChannel()!=null){ Map<String,Long> workspaces = diskUsage.slaveWorkspacesUsage.get(nodeName); Iterator<String> pathIterator = workspaces.keySet().iterator(); while(pathIterator.hasNext()){ String path = pathIterator.next(); try{ FilePath workspace = node.createPath(path); if(!workspace.exists()){ pathIterator.remove(); } } catch(Exception e){ LOGGER.log(Level.WARNING, "Can not check if file " + path + " exists on node " + node.getNodeName()); } } if(workspaces.isEmpty()){ iterator.remove(); } } } } saveDiskUsage(); } public Long getAllNonSlaveOrCustomWorkspaceSize(){ Long size = 0l; for(String nodeName: getSlaveWorkspaceUsage().keySet()){ Node node = null; if(nodeName.isEmpty()){ node = Jenkins.getInstance(); } else{ node = Jenkins.getInstance().getNode(nodeName); } if(node==null) //slave does not exist continue; Map<String,Long> paths = getSlaveWorkspaceUsage().get(nodeName); for(String path: paths.keySet()){ TopLevelItem item = null; if(owner instanceof TopLevelItem){ item = (TopLevelItem) owner; } else{ item = (TopLevelItem) owner.getParent(); } try{ if(!DiskUsageUtil.isContainedInWorkspace(item, node, path)){ size += paths.get(path); } } catch(Exception e){ LOGGER.log(Level.WARNING, "Can not get workspace for " + item.getDisplayName() + " on " + node.getDisplayName(), e); } } } return size; } public Long getAllWorkspaceSize(){ Long size = 0l; for(String nodeName: getSlaveWorkspaceUsage().keySet()){ Node slave = Jenkins.getInstance().getNode(nodeName); if(slave==null && !nodeName.isEmpty() && !(slave instanceof Jenkins)) {//slave does not exist continue; } Map<String,Long> paths = getSlaveWorkspaceUsage().get(nodeName); for(String path: paths.keySet()){ size += paths.get(path); } } return size; } // public Object readResolve() { // //ensure that build was not removed without calling listener - badly removed, or badly saved (without build.xml) // // for(DiskUsageBuildInformation information : diskUsage.getBuildDiskUsage(false)){ // File buildsDirectory = new File(owner.getRootDir(),"builds"); // File build = new File(buildsDirectory,information.toString()); // if(!build.exists()){ // diskUsage.removeBuild(information); // } // } // return this; // } public Long getDiskUsageWithoutBuilds(){ return diskUsage.getDiskUsageWithoutBuilds(); } public Long getAllDiskUsageWithoutBuilds(){ return DiskUsageUtil.getDiskUsageItemAction(owner).getAllDiskUsageWithoutBuilds(true); } private Long getDiskUsageWithoutBuildsAllSubItems(ItemGroup group){ Long usage = 0l; for(Object item: group.getItems()){ if(item instanceof ItemGroup){ ItemGroup subGroup = (ItemGroup) item; usage += getDiskUsageWithoutBuildsAllSubItems(subGroup); } if(item instanceof AbstractProject){ AbstractProject p = (AbstractProject) item; DiskUsageProperty property = (DiskUsageProperty) p.getProperty(DiskUsageProperty.class); if(property!=null){ usage += property.getDiskUsageWithoutBuilds(); } } } return usage; } @Initializer(after = InitMilestone.PLUGINS_STARTED) public static void transitionAuth() throws IOException { DiskUsageDescriptor that = (DiskUsageDescriptor) Hudson.getInstance().getDescriptor(DiskUsageProperty.class); if(that == null){ LOGGER.warning("Cannot convert DiskUsageProjectActions, DiskUsageDescripto is null, check log for previous DI error, e.g. Guice errors."); return; } if (!that.converted) { DiskUsageProjectActionFactory.DESCRIPTOR.setShowGraph(that.showGraph); that.converted = true; that.save(); DiskUsageProjectActionFactory.DESCRIPTOR.save(); } } public void saveDiskUsage() { diskUsage.save(); } public void loadDiskUsage(){ AbstractProject job = (AbstractProject) owner; diskUsage.load(); //ensure that build was not removed without calling listener - badly removed, or badly saved (without build.xml) for(DiskUsageBuildInformation information : diskUsage.getBuildDiskUsage(false)){ File buildsDirectory = new File(owner.getRootDir(),"builds"); File build = new File(buildsDirectory,information.getId()); if(!build.exists()){ diskUsage.removeBuild(information); } } diskUsage.save(); } @Override public JobProperty<?> reconfigure(StaplerRequest req, JSONObject form) throws Descriptor.FormException { return new DiskUsageProperty(); } @Extension public static final class DiskUsageDescriptor extends JobPropertyDescriptor { @Deprecated private boolean showGraph; @Deprecated private boolean converted; public DiskUsageDescriptor() { load(); } @Override public String getDisplayName() { return Messages.DisplayName(); } public boolean showGraph(){ return showGraph; } @Override public boolean isApplicable(Class<? extends Job> jobType) { return true; } } public static final Logger LOGGER = Logger.getLogger(DiskUsageProperty.class.getName()); }