RepoBenchmark.py #1

  • //
  • guest/
  • robert_cowham/
  • p4benchmark/
  • main/
  • RepoBenchmark.py
  • View
  • Commits
  • Open Download .zip Download (9 KB)
#! /usr/bin/env python2
#
# Main driver for benchmarks
#
# Copyright (C) 2016, Robert Cowham, Perforce
#

from __future__ import print_function

import logging
import os
import P4
import platform
import sys
import random
import string
import subprocess
from argparse import ArgumentParser
import yaml

SVN = "svn" # SVN command

def readConfig(startdir):
    config = {}
    with open(os.path.join(startdir, "benchmark.yaml"), "r") as f:
        config = yaml.load(f)
    return config

logger = logging.getLogger("console_logger")

if not (0x02070000 <= sys.hexversion < 0x02080000):
    sys.exit("Python 2.7 is required to run this program.")

LINE_LENGTH = 80
BLOCK_SIZE = 65536

# Random generators
try:
    import numpy as np

    def create_random(size):
        return bytes((int(x) for x in np.random.random_integers(1, 254, size)))

    def generator(size, eol="\n"):
        s = string.ascii_letters + string.digits
        return "".join(np.random.choice(list(s), size - 1)) + eol

except ImportError:
    print("No numpy installed, falling back to standard Python random. Prepare to wait ...", file=sys.stderr)

    def create_random(size):
        return bytes((random.randint(1,254) for x in range(size)))

    def generator(size, eol="\n"):
        s = string.ascii_letters + string.digits
        return "".join((random.choice(s) for x in range(size - 1))) + eol

def create_file(fileSize, filename, binary=False):
    mode = "wb" if binary else "w"
    with open(filename, mode) as f:
        if binary:
            blocks = int(fileSize / BLOCK_SIZE)
            for unused in range(blocks):
                b = create_random(BLOCK_SIZE)
                f.write( b )
            rest = fileSize % BLOCK_SIZE
            if rest:
                b = create_random(rest)
                f.write( b )
        else:
            lines = int(fileSize / LINE_LENGTH)
            for unused in range(lines):
                f.write(generator(LINE_LENGTH))

            rest = fileSize % LINE_LENGTH
            if rest:
                f.write(generator(rest))

class RepoBenchmark(object):
    """Generic benchmarking class - must be subclassed"""

    def __init__(self, startdir):
        self.id = id(self)      # Object id - unique enough for now.
        self.root = os.path.join(startdir, "testdir")
        self.localfilelist = []

    def createWorkspace(self, name):
        raise NotImplementedError("createWorkspace")

    def syncWorkspace(self):
        raise NotImplementedError("syncWorkspace")

    def addFile(self, filename):
        raise NotImplementedError("addFile")

    def editFile(self, filename):
        raise NotImplementedError("editFile")

    def deleteFile(self, filename):
        raise NotImplementedError("deleteFile")

    def basicFileActions(self):
        """Randomly edit/delete/add files in workspace"""
        for i in range(random.randint(1, 20)):
            action = random.choice(["add", "edit", "edit", "edit", "edit", "delete"])
            filename = random.choice(self.localfilelist)
            if action == "add":
                addfilename = os.path.join(os.path.dirname(filename), generator(20, eol=""))
                create_file(random.randint(100, 1000000), addfilename, random.choice([True, False]))
                self.addFile(addfilename)
            elif action == "edit":
                self.editFile(filename)
                with open(filename, "a") as f:
                    f.write("Some extra data\n")
            else:
                self.deleteFile(filename)

    def commit(self):
        """Commit/Submit changes"""
        raise NotImplementedError("commit")

    def run(self):
        """Run the appropriate test - basically a sync/edit/add/delete and submit"""
        try:
            self.createWorkspace("bench_ws")
            self.syncWorkspace()
            self.basicFileActions()
            self.commit()
        except Exception as e:
            logger.exception(e)
            raise e

class P4Benchmark(RepoBenchmark):
    """Performs basic benchmark test - Perforce specific subclass"""

    def __init__(self, startdir, config):
        super(P4Benchmark, self).__init__(startdir)
        self.p4 = P4.P4()
        self.p4.logger = logger
        p4config = config["perforce"]
        self.p4.port = p4config["port"]
        self.repoPath = p4config["repoPath"]
        self.p4.user = p4config["user"]
        logger.debug(self.p4.connect())
        if p4config["password"]:
            self.p4.run_login(p4config["password"])

    def createWorkspace(self, name):
        if not os.path.isdir(self.root):
            os.makedirs(self.root, 0o777)

        self.workspace_name = "{}.{}.{}".format(self.p4.user, self.id, platform.node())

        ws = self.p4.fetch_client(self.workspace_name)
        ws._root = os.path.join(self.root, self.workspace_name)
        ws._view = ["//depot/Jam/MAIN/... //{}/...".format(self.workspace_name)]
        ws._options = ws._options.replace("normdir", "rmdir")
        result = self.p4.save_client(ws)
        self.p4.client = self.workspace_name
        return result

    def syncWorkspace(self):
        with self.p4.at_exception_level(P4.P4.RAISE_ERRORS):
            self.p4.run_sync()
        havelist = self.p4.run_have()
        self.localfilelist = [f["path"] for f in havelist]

    def addFile(self, filename):
        self.p4.run_add(filename)

    def editFile(self, filename):
        self.p4.run_edit(filename)

    def deleteFile(self, filename):
        self.p4.run_delete(filename)

    def commit(self):
        """Submit our change"""
        if len(self.p4.run_opened()) > 0:
            chg = self.p4.fetch_change()
            chg._description = "A test change"
            self.p4.save_submit(chg)


class SvnBenchmark(RepoBenchmark):
    """Performs basic benchmark test - SVN specific subclass"""

    def __init__(self, startdir, config):
        super(SvnBenchmark, self).__init__(startdir)
        svnconfig = config["svn"]
        self.serverURL = svnconfig["serverURL"]
        self.repoPath = svnconfig["repoPath"]
        self.fullURL = "{}/{}".format(self.serverURL, self.repoPath)
        self.user = svnconfig["user"]
        self.password = svnconfig["password"]

    def svncmd(self, cmd, args, special = []):
        command = [SVN, cmd]
        for arg in args:
            command.append(arg)

        print("cmd:", command)
        logger.info(", ".join(command))
        p = subprocess.Popen(command, stdout = subprocess.PIPE)
        result = p.stdout.read()
        logger.debug(result)
        return result

    def createWorkspace(self, name):
        if not os.path.isdir(self.root):
            os.makedirs(self.root, 0o777)

        self.workspace_name = "{}.{}.{}".format(self.user, self.id, platform.node())
        self.workspace_root = os.path.join(self.root, self.workspace_name)

    def syncWorkspace(self):
        cmd = "co"
        if os.path.isdir(self.workspace_root):
            cmd = "update"
        self.svncmd(cmd,
                    ["--username", self.user, "--password", self.password,
                     self.fullURL,
                     self.workspace_root
                    ])
        self.localfilelist = []
        for root, dirs, files in os.walk(self.workspace_root):
            if ".svn" in dirs:
                dirs.remove(".svn")
            self.localfilelist.extend([os.path.join(root, f) for f in files])

    def addFile(self, filename):
        os.chdir(self.workspace_root)
        self.svncmd("add", [filename])

    def editFile(self, filename):
        logger.info("svn edit")
        pass    # SVN doesn't do edits

    def deleteFile(self, filename):
        os.chdir(self.workspace_root)
        self.svncmd("delete", [filename])

    def commit(self):
        description = "SVN checkin"
        os.chdir(self.workspace_root)
        self.svncmd("ci", ["-m", description])

# For testing purposes - normally expected to be imported
if __name__ == "__main__":
    global logger
    parser = ArgumentParser(
            description="Benchmark Testing",
            epilog="Copyright (C) 2016 Robert Cowham, Perforce Software Ltd")
    parser.add_argument("-s","--svn", action='store_true', default=False, help="Run SVN")
    parser.add_argument("-p","--p4", action='store_true', default=True, help="Run P4")
    options = parser.parse_args()

    # create console handler
    console_logger = logging.getLogger("console_logger")
    sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG)
    console_logger.addHandler(sh)
    console_logger.info("running at top level")
    console_logger.propagate = False
    logger = console_logger

    startdir = os.getcwd()
    if options.svn:
        b = SvnBenchmark(startdir, readConfig(startdir))
    else:
        b = P4Benchmark(startdir, readConfig(startdir))
    try:
        b.run()
    except Exception as e:
        pass
# Change User Description Committed
#11 24711 Robert Cowham Restructure and tidy up
#10 21717 Robert Cowham Trying nested tasks
#9 21692 Robert Cowham MAke more robust for concurrent usage
#8 21665 Robert Cowham Make binary files really binary with python2
#7 20615 Robert Cowham Remove hardcoded path
#6 19798 Robert Cowham Log exceptions.
Refactor Svn as well
#5 19796 Robert Cowham Refactored into taskset
#4 19791 Robert Cowham Fix logging.
Unset P4CONFIG in env to avoid DVCS issues
#3 19779 Robert Cowham Refactor - move test root
#2 19778 Robert Cowham Renamed config file and added svnexe to it
#1 19773 Robert Cowham Initial version of benchmark scripts