package hudson.plugins.disk_usage; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Item; import hudson.model.Run; import hudson.model.ItemGroup; import hudson.model.Node; import hudson.model.ProminentProjectAction; import hudson.model.TopLevelItem; import hudson.plugins.disk_usage.unused.DiskUsageItemGroup; import hudson.util.Graph; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import jenkins.model.Jenkins; import org.jfree.data.category.DefaultCategoryDataset; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.ExportedBean; /** * Disk usage of a project * * @author dvrzalik */ @ExportedBean(defaultVisibility = 1) public class ProjectDiskUsageAction implements ProminentProjectAction, DiskUsageItemAction { AbstractProject<? extends AbstractProject, ? extends AbstractBuild> project; private ProjectDiskUsage diskUsage; public ProjectDiskUsageAction(AbstractProject<? extends AbstractProject, ? extends AbstractBuild> project) { this.project = project; DiskUsageProperty property = project.getProperty(DiskUsageProperty.class); if(property==null){ property = new DiskUsageProperty(); try { project.addProperty(property); property.getDiskUsage().load(); } catch (IOException ex) { Logger.getLogger(ProjectDiskUsageAction.class.getName()).log(Level.SEVERE, null, ex); } } diskUsage = property.getDiskUsage(); } public String getIconFileName() { return null; } public String getDisplayName() { return Messages.DisplayName(); } public String getUrlName() { return Messages.UrlName(); } public Long getAllSlaveWorkspaces(){ return getAllSlaveWorkspaces(true); } public Long getAllSlaveWorkspaces(boolean cashed){ return getAllDiskUsageWorkspace(cashed) - getAllCustomOrNonSlaveWorkspaces(cashed); } @Override public Long getAllCustomOrNonSlaveWorkspaces(boolean cashed){ if(cashed){ return diskUsage.getCashedDiskUsageNonSlaveWorkspace(); } Long size = 0l; for(String nodeName: diskUsage.getSlaveWorkspacesUsage().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 = diskUsage.getSlaveWorkspacesUsage().get(nodeName); for(String path: paths.keySet()){ TopLevelItem item = null; if(project instanceof TopLevelItem){ item = (TopLevelItem) project; } else{ item = (TopLevelItem) project.getParent(); } try{ if(!DiskUsageUtil.isContainedInWorkspace(item, node, path)){ size += paths.get(path); } } catch(Exception e){ Logger.getLogger(getClass().getName()).log(Level.WARNING, "Can not get workspace for " + item.getDisplayName() + " on " + node.getDisplayName(), e); } } } if(project instanceof ItemGroup){ ItemGroup group = (ItemGroup) project; for(Object i:group.getItems()){ if(i instanceof AbstractProject){ AbstractProject p = (AbstractProject) i; ProjectDiskUsageAction action = p.getAction(ProjectDiskUsageAction.class); if(action!=null){ size += action.getAllCustomOrNonSlaveWorkspaces(cashed); } } } } diskUsage.setCashedDiskUsageNonSlaveWorkspace(size); return size; } /** * Returns all workspace disku usage including workspace usage its sub-projects * * @return disk usage project and its sub-projects */ @Override public Long getAllDiskUsageWorkspace(boolean cashed){ if(cashed){ return diskUsage.getCashedDiskUsageWorkspace(); } Long size = 0l; for(String nodeName: diskUsage.getSlaveWorkspacesUsage().keySet()){ Node slave = Jenkins.getInstance().getNode(nodeName); if(slave==null && !nodeName.isEmpty() && !(slave instanceof Jenkins)) {//slave does not exist continue; } Map<String,Long> paths = diskUsage.getSlaveWorkspacesUsage().get(nodeName); for(String path: paths.keySet()){ size += paths.get(path); } } if(project instanceof ItemGroup){ ItemGroup group = (ItemGroup) project; for(Object i:group.getItems()){ if(i instanceof AbstractProject){ AbstractProject p = (AbstractProject) i; ProjectDiskUsageAction action = (ProjectDiskUsageAction) p.getAction(ProjectDiskUsageAction.class); if(action!=null){ size += action.getAllDiskUsageWorkspace(cashed); } } } } diskUsage.setCashedDiskUsageWorkspace(size); return size; } public String getSizeInString(Long size){ return DiskUsageUtil.getSizeString(size); } public Long getDiskUsageWithoutBuilds(){ return getAllDiskUsageWithoutBuilds(true); } public Long getAllDiskUsageWithoutBuilds(){ return getAllDiskUsageWithoutBuilds(true); } @Override public Long getAllDiskUsageWithoutBuilds(boolean cashed){ if(cashed){ return diskUsage.getCashedDiskUsageWithoutBuilds(); } Long size = 0l; if(project instanceof ItemGroup){ size += DiskUsageUtil.getItemGroupAction((ItemGroup)project).getAllDiskUsageWithoutBuilds(cashed); } else{ size += diskUsage.getDiskUsageWithoutBuilds(); } diskUsage.setCashedDiskUsageWithoutBuilds(size); return size; } public Long getJobRootDirDiskUsage(boolean cashed) { return getBuildsDiskUsage(cashed).get("all") + getDiskUsageWithoutBuilds(); } public DiskUsageProperty getDiskUsageProperty(){ DiskUsageProperty property = (DiskUsageProperty) project.getProperty(DiskUsageProperty.class); return property; } public ProjectDiskUsage getDiskUsage(){ return diskUsage; } public Long getAllDiskUsageNotLoadedBuilds(boolean cashed) { return getBuildsDiskUsage(cashed).get("notLoaded"); } //todo better to do check somewhere else,it is used for view level too private Map<String,Long> getBuildsDiskUsageAllSubItems(ItemGroup group, Date older, Date yonger) { Map<String,Long> usage = new TreeMap<String,Long>(); Long buildsDiskUsage = 0l; Long locked = 0l; Long notLoaded = 0L; for(Object item: group.getItems()){ if(item instanceof ItemGroup){ ItemGroup subGroup = (ItemGroup) item; buildsDiskUsage += getBuildsDiskUsageAllSubItems(subGroup, older, yonger).get("all"); locked += getBuildsDiskUsageAllSubItems(subGroup, older, yonger).get("locked"); notLoaded += getBuildsDiskUsageAllSubItems(subGroup, older, yonger).get("notLoaded"); } else{ if(group instanceof AbstractProject){ AbstractProject p = (AbstractProject) item; ProjectDiskUsage pUsage = p.getAction(ProjectDiskUsageAction.class).getDiskUsage(); Set<DiskUsageBuildInformation> informations = pUsage.getBuildDiskUsage(false); for(DiskUsageBuildInformation information: informations){ Date date = new Date(information.getTimestamp()); if(older!=null && !date.before(older)) continue; if(yonger!=null && !date.after(yonger)) continue; Long size = information.getSize(); buildsDiskUsage += size; Collection<AbstractBuild> loadedBuilds = (Collection<AbstractBuild>) p._getRuns().getLoadedBuilds().values(); AbstractBuild build = null; for (AbstractBuild b : loadedBuilds){ if(b.getId().equals(information.getId())){ build = b; } } if(build!=null && build.isKeepLog()){ locked += size; } else{ if(information.isLocked()){ locked += size; } } } for(File file : diskUsage.getFilesOfNotLoadedBuilds()){ GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeInMillis(file.lastModified()); Date date = calendar.getTime(); if(older!=null && !date.before(older)) continue; if(yonger!=null && !date.after(yonger)) continue; Long size = diskUsage.getSizeOfNotLoadedBuild(file.getName()); buildsDiskUsage += size; notLoaded += size; } } } } usage.put("all", buildsDiskUsage); usage.put("locked", locked); usage.put("notLoaded", notLoaded); return usage; } public Map<String, Long> getBuildsDiskUsage() throws IOException { return getBuildsDiskUsage(null, null, true); } public Map<String, Long> getBuildsDiskUsage(boolean cashed) { return getBuildsDiskUsage(null, null, cashed); } public Long getAllBuildsDiskUsage(boolean cashed) { return getBuildsDiskUsage(null, null,cashed).get("all"); } public Long getAllBuildsDiskUsage() { return getBuildsDiskUsage(null, null,true).get("all"); } public Map<String, Long> getBuildsDiskUsage(Date older, Date yonger) { return getBuildsDiskUsage(older, yonger, true); } public Long getDiskUsageWorkspace(){ return getAllDiskUsageWorkspace(); } public Long getAllDiskUsageWorkspace(){ return getAllDiskUsageWorkspace(true); } /** * @return Disk usage for all builds */ @Override public Map<String, Long> getBuildsDiskUsage(Date older, Date younger, boolean cashed) { if(cashed && older==null && younger == null){ // it is necessary go grab all information if it is filtered return diskUsage.getCashedBuildDiskUsage(); } Map<String,Long> usage = new TreeMap<String,Long>(); Long buildsDiskUsage = 0l; Long locked = 0l; Long notLoaded = 0l; if (project != null) { if(project instanceof ItemGroup){ ItemGroup group = (ItemGroup) project; Map<String,Long> sizes = getBuildsDiskUsageAllSubItems(group, older, younger); buildsDiskUsage += sizes.get("all"); locked += sizes.get("locked"); notLoaded += sizes.get("notLoaded"); } Set<DiskUsageBuildInformation> informations = diskUsage.getBuildDiskUsage(false); for(DiskUsageBuildInformation information: informations){ Date date = new Date(information.getTimestamp()); if(older!=null && !date.before(older)) continue; if(younger!=null && !date.after(younger)) continue; Long size = information.getSize(); buildsDiskUsage += size; Collection<AbstractBuild> loadedBuilds = (Collection<AbstractBuild>) project._getRuns().getLoadedBuilds().values(); AbstractBuild build = null; for (AbstractBuild b : loadedBuilds){ if(b.getId().equals(information.getId())){ build = b; } } if(build!=null && build.isKeepLog()){ locked += size; } else{ if(information.isLocked()){ locked += size; } } } for(File file : diskUsage.getFilesOfNotLoadedBuilds()){ GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeInMillis(file.lastModified()); Date date = calendar.getTime(); if(older!=null && !date.before(older)) continue; if(younger!=null && !date.after(younger)) continue; Long size = diskUsage.getSizeOfNotLoadedBuild(file.getName()); buildsDiskUsage += size; notLoaded += size; } } usage.put("all", buildsDiskUsage); usage.put("locked", locked); usage.put("notLoaded", notLoaded); diskUsage.setCashedBuildDiskUsage(usage); return usage; } public BuildDiskUsageAction getLastBuildAction() { Run run = project.getLastBuild(); if (run != null) { return run.getAction(BuildDiskUsageAction.class); } return null; } public Set<DiskUsageBuildInformation> getBuildsInformation() throws IOException{ return diskUsage.getBuildDiskUsage(false); } /** * Generates a graph with disk usage trend * */ public Graph getGraph() throws IOException { //TODO if(nothing_changed) return; List<Object[]> usages = new ArrayList<Object[]>(); long maxValue = 0; long maxValueWorkspace = 0; maxValueWorkspace = Math.max(getAllCustomOrNonSlaveWorkspaces(true), getAllSlaveWorkspaces(true)); Long jobRootDirDiskUsage = getJobRootDirDiskUsage(true); maxValue = jobRootDirDiskUsage; //First iteration just to get scale of the y-axis ArrayList<DiskUsageBuildInformation> builds = new ArrayList<DiskUsageBuildInformation>(); builds.addAll(diskUsage.getBuildDiskUsage(false)); //do it in reverse order for (int i=builds.size()-1; i>=0; i--) { DiskUsageBuildInformation build = builds.get(i); Long usage = diskUsage.getDiskUsageBuildInformation(build.getId()).getSize(); usages.add(new Object[]{build.getNumber(), getJobRootDirDiskUsage(true), usage, getAllSlaveWorkspaces(true), getAllCustomOrNonSlaveWorkspaces(true)}); maxValue = Math.max(maxValue, usage); } int floor = (int) DiskUsageUtil.getScale(maxValue); String unit = DiskUsageUtil.getUnitString(floor); int workspaceFloor = (int) DiskUsageUtil.getScale(maxValueWorkspace); String workspaceUnit = DiskUsageUtil.getUnitString(workspaceFloor); double base = Math.pow(1024, floor); double workspaceBase = Math.pow(1024, workspaceFloor); DefaultCategoryDataset dataset = new DefaultCategoryDataset(); DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); for (Object[] usage : usages) { Integer label = ((Integer) usage[0]); dataset.addValue(((Long) usage[1]) / base, Messages.DiskUsage_Graph_JobDirectory(), label); dataset.addValue(((Long) usage[2]) / base, Messages.DiskUsage_Graph_BuildDirectory(), label); dataset2.addValue(((Long) usage[3]) / workspaceBase, Messages.DiskUsage_Graph_SlaveWorkspaces(), label); dataset2.addValue(((Long) usage[4]) / workspaceBase, Messages.DiskUsage_Graph_NonSlaveWorkspaces(), label); } return new DiskUsageGraph(dataset, unit, dataset2, workspaceUnit); } public void doDelete(StaplerRequest req, StaplerResponse res) throws IOException, ServletException{ String buildId = req.getParameter("buildId"); File file = new File(project.getBuildDir(), buildId); Util.deleteRecursive(file); getDiskUsage().removeDeletedNotLoadedBuild(buildId); req.getView(this, "index.jelly").forward(req, res); } public void doReload(StaplerRequest req, StaplerResponse res) throws IOException, ServletException{ String buildId = req.getParameter("buildId"); AbstractBuild build = project.getBuild(buildId); if(build==null){ req.setAttribute("errorMessage","Build " + buildId+ " can not be loaded. Please, check Jenkins log for details."); } else{ getDiskUsage().moveToLoadedBuilds(build, getDiskUsage().getSizeOfNotLoadedBuild(buildId)); } req.getView(this, "index.jelly").forward(req, res); } @Override public Long getAllDiskUsage(boolean cashed) { Long size = getAllBuildsDiskUsage(cashed) + getAllDiskUsageWithoutBuilds(cashed) + getAllDiskUsageNotLoadedBuilds(cashed); if(project instanceof ItemGroup){ DiskUsageItemGroup group = DiskUsageUtil.getItemGroupAction((ItemGroup)project).getDiskUsageItemGroup(); size += group.getDiskUsageOfNotLoadedJobs(cashed); } return size; } /** Shortcut for the jelly view */ public boolean showGraph() { return Jenkins.getInstance().getPlugin(DiskUsagePlugin.class).getConfiguration().isShowGraph(); } private void actualizeCashedData(boolean parent) { diskUsage.setCashedBuildDiskUsage(getBuildsDiskUsage(false)); diskUsage.setCashedDiskUsageNonSlaveWorkspace(getAllCustomOrNonSlaveWorkspaces(false)); diskUsage.setCashedDiskUsageWithoutBuilds(getAllDiskUsageWithoutBuilds(false)); diskUsage.setCashedDiskUsageWorkspace(getAllDiskUsageWorkspace(false)); ItemGroup group = project.getParent(); if(group!=null && parent){ DiskUsageUtil.getItemGroupAction(group).actualizeCashedData(); } } @Override public void actualizeCashedData() { actualizeCashedData(true); } @Override public void actualizeCashedBuildsData() { diskUsage.setCashedBuildDiskUsage(getBuildsDiskUsage(null, null, false)); if(project.getParent() != null){ DiskUsageUtil.getItemGroupAction(project.getParent()).actualizeCashedBuildsData(); } } @Override public void actualizeCashedWorkspaceData() { diskUsage.setCashedDiskUsageWorkspace(getAllDiskUsageWorkspace(false)); if(project.getParent() != null){ DiskUsageUtil.getItemGroupAction(project.getParent()).actualizeCashedWorkspaceData(); } } @Override public void actualizeCashedNotCustomWorkspaceData() { diskUsage.setCashedDiskUsageNonSlaveWorkspace(getAllCustomOrNonSlaveWorkspaces(false)); if(project.getParent() != null){ DiskUsageUtil.getItemGroupAction(project.getParent()).actualizeCashedNotCustomWorkspaceData(); } } @Override public void actualizeCashedJobWithoutBuildsData() { diskUsage.setCashedDiskUsageWithoutBuilds(getAllDiskUsageWithoutBuilds(false)); if(project.getParent() != null){ DiskUsageUtil.getItemGroupAction(project.getParent()).actualizeCashedJobWithoutBuildsData(); } } @Override public void actualizeAllCashedDate() { actualizeCashedJobWithoutBuildsData(); actualizeCashedNotCustomWorkspaceData(); actualizeCashedWorkspaceData(); actualizeCashedBuildsData(); } }