#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (c) 2013 Sven Erik Knop, Perforce Software Ltd # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE # SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # User contributed content on the Perforce Public Depot is not supported by Perforce, # although it may be supported by its author. This applies to all contributions # even those submitted by Perforce employees. # # Maintainer: Sven Erik Knop, sknop@perforce.com import P4 import sys import argparse class Context: def __init__(self, revisions, summary): self.revisions = revisions self.summary = summary self.p4 = P4.P4() self.p4.prog = "Directory Sizes" self.p4.connect() self.width = 0 def update_width(self, new_width): self.width = max(self.width, new_width) class Directory: def __init__(self, context, path, totalDepth, myDepth = 0): self.context = context self.path = path self.totalDepth = totalDepth self.myDepth = myDepth self.path_indent = "{indent}{path}".format(indent = " ... " * self.myDepth, path = self.path) self.context.update_width(len(self.path_indent)) self.dirs = [] if totalDepth > 0: self.dirs = self.find_directories() self.size = 0 if self.dirs: self.size = self.run_sizes("*") for subdir in self.dirs: self.size += subdir.size else: self.size = self.run_sizes("...") def find_directories(self): dirs = [] for d in self.context.p4.run_dirs(self.path + "/*"): dirs.append( Directory(self.context, d['dir'], self.totalDepth - 1, self.myDepth + 1) ) return dirs def run_sizes(self, wildcard): cmd = "{dir}/{wild}{range}".format(dir=self.path, wild=wildcard, range=self.context.revisions) return int(self.context.p4.run_sizes('-a', self.context.summary, cmd)[0]['fileSize']) def human(self): units = [" B", " KB", " MB", " GB", " TB"] factor = 1024 current = self.size for u in units: if current < factor: return str(current) + u else: current = round(current / factor, 2) return str(current) + " PB" def print_size(self): print("{p:{width}} : {size:16} ({human})".\ format(p = self.path_indent, width = self.context.width, size = self.size, human = self.human() ) ) for d in self.dirs: d.print_size() if __name__ == "__main__": parser = argparse.ArgumentParser(description="Directory sizes") parser.add_argument("-d","--depth", default=1, type=int) parser.add_argument("-p","--path", required=True, help="Provide path without wildcard, for example //depot") parser.add_argument("-r","--range", default="", help="Perforce style revision range, start with @ or #") parser.add_argument("-z","--omit-lazy", action='store_const', default="-s", const="-z", help="Omit lazy copies") args = parser.parse_args() start_dir = args.path if args.path.endswith("/"): start_dir = args.path[:-1] root = Directory(Context(args.range, args.omit_lazy), start_dir, args.depth) root.print_size()
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 8808 | Robert Cowham | Branching using cowhamr.sven.utils | ||
//guest/sven_erik_knop/P4Pythonlib/scripts/directory_sizes.py | |||||
#6 | 8294 | Sven Erik Knop |
Updated directory_sizes with -z option and cleaned up some of the code. Should be working fine for everyone now :-) |
||
#5 | 8293 | Sven Erik Knop |
... and now with proper formatting as well. I declare this version finished. Have fun :-) |
||
#4 | 8292 | Sven Erik Knop |
Much better version of the directory_sizes script. Now recursive, easier to understand and maintain ... and faster I believe. Only thing missing now is proper formatting. |
||
#3 | 8291 | Sven Erik Knop |
This is a nice try, but going the wrong way. We need a recursive algorithm, which will be simpler yet more powerful. |
||
#2 | 8287 | Sven Erik Knop | Updated copyright and no warranty notice. | ||
#1 | 8283 | Sven Erik Knop |
Set of useful scripts and triggers in P4Python. Most require Python 3.X to run, but can be made to use Python 2.7 if necessary. The scripts will also need P4Python 2012.2 + to work. changes_month_depot analysis the usage in changes and bytes of a Perforce Server. directory_sizes list the sizes of files within a directory of Perforce hash_database attempts to find identical files submitted several times (not lazy copies) open_view_clients lists all clients with open views Run the first 3 scripts against a (db only sufficient) replica, they can be very heavy on a server. The last file is a simple client form-in trigger to prevent open views. |