base_job.py revision fa2e889be2e704a8b3c723900f26cfe33a9cbb7d
1fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanskiimport os, copy, logging, errno, cPickle as pickle, fcntl 2da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 3da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanskifrom autotest_lib.client.common_lib import autotemp, error 4da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 5da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 6da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanskiclass job_directory(object): 7da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """Represents a job.*dir directory.""" 8da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 9da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 10fc3da5bbdcd63840cd224282812c050c5520db11mbligh class JobDirectoryException(error.AutotestError): 11fc3da5bbdcd63840cd224282812c050c5520db11mbligh """Generic job_directory exception superclass.""" 12fc3da5bbdcd63840cd224282812c050c5520db11mbligh 13fc3da5bbdcd63840cd224282812c050c5520db11mbligh 14fc3da5bbdcd63840cd224282812c050c5520db11mbligh class MissingDirectoryException(JobDirectoryException): 15da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """Raised when a directory required by the job does not exist.""" 16da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def __init__(self, path): 17da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Exception.__init__(self, 'Directory %s does not exist' % path) 18da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 19da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 20fc3da5bbdcd63840cd224282812c050c5520db11mbligh class UncreatableDirectoryException(JobDirectoryException): 21da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """Raised when a directory required by the job is missing and cannot 22da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski be created.""" 23da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def __init__(self, path, error): 24da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski msg = 'Creation of directory %s failed with exception %s' 25da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski msg %= (path, error) 26da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Exception.__init__(self, msg) 27da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 28da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 29fc3da5bbdcd63840cd224282812c050c5520db11mbligh class UnwritableDirectoryException(JobDirectoryException): 30da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """Raised when a writable directory required by the job exists 31da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski but is not writable.""" 32da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def __init__(self, path): 33da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski msg = 'Directory %s exists but is not writable' % path 34da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Exception.__init__(self, msg) 35da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 36da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 37da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def __init__(self, path, is_writable=False): 38da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 39da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Instantiate a job directory. 40da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 41da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @param path The path of the directory. If None a temporary directory 42da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski will be created instead. 43da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @param is_writable If True, expect the directory to be writable. 44da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 45da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises MissingDirectoryException raised if is_writable=False and the 46da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski directory does not exist. 47da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises UnwritableDirectoryException raised if is_writable=True and 48da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski the directory exists but is not writable. 49da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises UncreatableDirectoryException raised if is_writable=True, the 50da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski directory does not exist and it cannot be created. 51da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 52da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if path is None: 53da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if is_writable: 54da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._tempdir = autotemp.tempdir(unique_id='autotest') 55da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self.path = self._tempdir.name 56da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski else: 57da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski raise self.MissingDirectoryException(path) 58da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski else: 59da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._tempdir = None 60da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self.path = path 61da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._ensure_valid(is_writable) 62da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 63da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 64da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def _ensure_valid(self, is_writable): 65da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 66da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Ensure that this is a valid directory. 67da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 68da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Will check if a directory exists, can optionally also enforce that 69da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski it be writable. It can optionally create it if necessary. Creation 70da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski will still fail if the path is rooted in a non-writable directory, or 71da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if a file already exists at the given location. 72da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 73da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @param dir_path A path where a directory should be located 74da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @param is_writable A boolean indicating that the directory should 75da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski not only exist, but also be writable. 76da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 77da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises MissingDirectoryException raised if is_writable=False and the 78da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski directory does not exist. 79da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises UnwritableDirectoryException raised if is_writable=True and 80da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski the directory is not wrtiable. 81da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises UncreatableDirectoryException raised if is_writable=True, the 82da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski directory does not exist and it cannot be created 83da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 848054b0da315c94d249aaf80512bc133d4cb55b92mbligh # ensure the directory exists 858054b0da315c94d249aaf80512bc133d4cb55b92mbligh if is_writable: 868054b0da315c94d249aaf80512bc133d4cb55b92mbligh try: 878054b0da315c94d249aaf80512bc133d4cb55b92mbligh os.makedirs(self.path) 888054b0da315c94d249aaf80512bc133d4cb55b92mbligh except OSError, e: 89fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh if e.errno != errno.EEXIST or not os.path.isdir(self.path): 90da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski raise self.UncreatableDirectoryException(self.path, e) 918054b0da315c94d249aaf80512bc133d4cb55b92mbligh elif not os.path.isdir(self.path): 928054b0da315c94d249aaf80512bc133d4cb55b92mbligh raise self.MissingDirectoryException(self.path) 93da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 94da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # if is_writable=True, also check that the directory is writable 95da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if is_writable and not os.access(self.path, os.W_OK): 96da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski raise self.UnwritableDirectoryException(self.path) 97da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 98da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 99da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @staticmethod 100da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def property_factory(attribute): 101da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 102da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Create a job.*dir -> job._*dir.path property accessor. 103da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 104da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @param attribute A string with the name of the attribute this is 105da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski exposed as. '_'+attribute must then be attribute that holds 106da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski either None or a job_directory-like object. 107da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 108da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @returns A read-only property object that exposes a job_directory path 109da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 110da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @property 111da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def dir_property(self): 112da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski underlying_attribute = getattr(self, '_' + attribute) 113da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if underlying_attribute is None: 114da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski return None 115da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski else: 116da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski return underlying_attribute.path 117da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski return dir_property 118da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 119da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 120fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mblighclass job_state(object): 121fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """A class for managing explicit job and user state, optionally persistent. 122fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 123fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh The class allows you to save state by name (like a dictionary). Any state 124fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh stored in this class should be picklable and deep copyable. While this is 125fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh not enforced it is recommended that only valid python identifiers be used 126fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh as names. Additionally, the namespace 'stateful_property' is used for 127fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh storing the valued associated with properties constructed using the 128fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh property_factory method. 129fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 130fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 131fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh NO_DEFAULT = object() 132fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh PICKLE_PROTOCOL = 2 # highest protocol available in python 2.4 133fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 134fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 135fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def __init__(self): 136fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Initialize the job state.""" 137fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self._state = {} 138fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self._backing_file = None 139fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_initialized = False 140fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_lock = None 141fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 142fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 143fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def _with_backing_file(method): 144fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """A decorator to perform a lock-read-*-write-unlock cycle. 145fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 146fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski When applied to a method, this decorator will automatically wrap 147fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski calls to the method in a lock-and-read before the call followed by a 148fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski write-and-unlock. Any operation that is reading or writing state 149fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski should be decorated with this method to ensure that backing file 150fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski state is consistently maintained. 151fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 152fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def wrapped_method(self, *args, **dargs): 153fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski already_have_lock = self._backing_file_lock is not None 154fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if not already_have_lock: 155fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._lock_backing_file() 156fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski try: 157fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._read_from_backing_file() 158fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski try: 159fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski return method(self, *args, **dargs) 160fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski finally: 161fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._write_to_backing_file() 162fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski finally: 163fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if not already_have_lock: 164fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._unlock_backing_file() 165fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski wrapped_method.__name__ = method.__name__ 166fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski wrapped_method.__doc__ = method.__doc__ 167fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski return wrapped_method 168fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 169fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 170fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def _lock_backing_file(self): 171fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """Acquire a lock on the backing file.""" 172fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if self._backing_file: 173fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_lock = open(self._backing_file, 'a') 174fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski fcntl.lockf(self._backing_file_lock, fcntl.LOCK_EX) 175fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 176fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 177fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def _unlock_backing_file(self): 178fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """Release a lock on the backing file.""" 179fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if self._backing_file_lock: 180fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski fcntl.lockf(self._backing_file_lock, fcntl.LOCK_UN) 181fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_lock.close() 182fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_lock = None 183fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 184fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 185fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def read_from_file(self, file_path, merge=True): 186fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Read in any state from the file at file_path. 187fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 188fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski When merge=True, any state specified only in-memory will be preserved. 189fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski Any state specified on-disk will be set in-memory, even if an in-memory 190fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski setting already exists. 191fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 192fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @param file_path The path where the state should be read from. It must 193fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski exist but it can be empty. 194fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @param merge If true, merge the on-disk state with the in-memory 195fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski state. If false, replace the in-memory state with the on-disk 196fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski state. 197fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 198fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @warning This method is intentionally concurrency-unsafe. It makes no 199fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski attempt to control concurrent access to the file at file_path. 200fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 201fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 202fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski # we can assume that the file exists 203fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if os.path.getsize(file_path) == 0: 204fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski on_disk_state = {} 205fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh else: 206fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski on_disk_state = pickle.load(open(file_path)) 207fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 208fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if merge: 209fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski # merge the on-disk state with the in-memory state 210fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski for namespace, namespace_dict in on_disk_state.iteritems(): 211fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski in_memory_namespace = self._state.setdefault(namespace, {}) 212fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski for name, value in namespace_dict.iteritems(): 213fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if name in in_memory_namespace: 214fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if in_memory_namespace[name] != value: 215fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski logging.info('Persistent value of %s.%s from %s ' 216fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 'overridding existing in-memory ' 217fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 'value', namespace, name, file_path) 218fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski in_memory_namespace[name] = value 219fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski else: 220fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski logging.debug('Value of %s.%s is unchanged, ' 221fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 'skipping import', namespace, name) 222fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh else: 223fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski logging.debug('Importing %s.%s from state file %s', 224fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski namespace, name, file_path) 225fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski in_memory_namespace[name] = value 226fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski else: 227fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski # just replace the in-memory state with the on-disk state 228fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._state = on_disk_state 229fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski logging.debug('Replacing in-memory state with on-disk state ' 230fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 'from %s', file_path) 231fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 232fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self._write_to_backing_file() 233fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 234fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 235fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def write_to_file(self, file_path): 236fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Write out the current state to the given path. 237fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 238fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param file_path The path where the state should be written out to. 239fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh Must be writable. 240fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 241fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @warning This method is intentionally concurrency-unsafe. It makes no 242fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski attempt to control concurrent access to the file at file_path, or 243fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski to the backing file if one exists. 244fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 245fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh outfile = open(file_path, 'w') 246fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh try: 247fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh pickle.dump(self._state, outfile, self.PICKLE_PROTOCOL) 248fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh finally: 249fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh outfile.close() 250fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh logging.debug('Persistent state flushed to %s', file_path) 251fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 252fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 253fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def _read_from_backing_file(self): 254fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """Refresh the current state from the backing file. 255fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 256fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski If the backing file has never been read before (indicated by checking 257fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_initialized) it will merge the file with the 258fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski in-memory state, rather than overwriting it. 259fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """ 260fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if self._backing_file: 261fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski merge_backing_file = not self._backing_file_initialized 262fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self.read_from_file(self._backing_file, merge=merge_backing_file) 263fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_initialized = True 264fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 265fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 266fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def _write_to_backing_file(self): 267fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Flush the current state to the backing file.""" 268fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh if self._backing_file: 269fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self.write_to_file(self._backing_file) 270fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 271fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 272fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @_with_backing_file 273fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def _synchronize_backing_file(self): 274fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """Synchronizes the contents of the in-memory and on-disk state.""" 275fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski # state is implicitly synchronized in _with_backing_file methods 276fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski pass 277fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 278fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 279fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def set_backing_file(self, file_path): 280fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Change the path used as the backing file for the persistent state. 281fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 282fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh When a new backing file is specified if a file already exists then 283fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh its contents will be added into the current state, with conflicts 284fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh between the file and memory being resolved in favor of the file 285fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh contents. The file will then be kept in sync with the (combined) 286fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh in-memory state. The syncing can be disabled by setting this to None. 287fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 288fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param file_path A path on the filesystem that can be read from and 289fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh written to, or None to turn off the backing store. 290fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 291fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._synchronize_backing_file() 292fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self._backing_file = file_path 293fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._backing_file_initialized = False 294fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski self._synchronize_backing_file() 295fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 296fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 297fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @_with_backing_file 298fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski def get(self, namespace, name, default=NO_DEFAULT): 299fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """Returns the value associated with a particular name. 300fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 301fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @param namespace The namespace that the property should be stored in. 302fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @param name The name the value was saved with. 303fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @param default A default value to return if no state is currently 304fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski associated with var. 305fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski 306fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @returns A deep copy of the value associated with name. Note that this 307fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski explicitly returns a deep copy to avoid problems with mutable 308fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski values; mutations are not persisted or shared. 309fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @raises KeyError raised when no state is associated with var and a 310fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski default value is not provided. 311fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski """ 312fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski if self.has(namespace, name): 313fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski return copy.deepcopy(self._state[namespace][name]) 314fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski elif default is self.NO_DEFAULT: 315fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski raise KeyError('No key %s in namespace %s' % (name, namespace)) 316fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski else: 317fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski return default 318fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 319fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 320fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @_with_backing_file 321fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def set(self, namespace, name, value): 322fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Saves the value given with the provided name. 323fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 324fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param namespace The namespace that the property should be stored in. 325fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param name The name the value should be saved with. 326fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param value The value to save. 327fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 328fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh namespace_dict = self._state.setdefault(namespace, {}) 329fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh namespace_dict[name] = copy.deepcopy(value) 330fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh logging.debug('Persistent state %s.%s now set to %r', namespace, 331fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh name, value) 332fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 333fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 334fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @_with_backing_file 335fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def has(self, namespace, name): 336fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Return a boolean indicating if namespace.name is defined. 337fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 338fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param namespace The namespace to check for a definition. 339fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param name The name to check for a definition. 340fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 341fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @returns True if the given name is defined in the given namespace and 342fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh False otherwise. 343fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 344fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh return namespace in self._state and name in self._state[namespace] 345fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 346fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 347fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @_with_backing_file 348fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def discard(self, namespace, name): 349fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """If namespace.name is a defined value, deletes it. 350fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 351fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param namespace The namespace that the property is stored in. 352fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param name The name the value is saved with. 353fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 354fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh if self.has(namespace, name): 355fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh del self._state[namespace][name] 356fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh if len(self._state[namespace]) == 0: 357fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh del self._state[namespace] 358fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh logging.debug('Persistent state %s.%s deleted', namespace, name) 359fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh else: 360fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh logging.debug( 361fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 'Persistent state %s.%s not defined so nothing is discarded', 362fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh namespace, name) 363fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 364fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 365fa2e889be2e704a8b3c723900f26cfe33a9cbb7djadmanski @_with_backing_file 366fc3da5bbdcd63840cd224282812c050c5520db11mbligh def discard_namespace(self, namespace): 367fc3da5bbdcd63840cd224282812c050c5520db11mbligh """Delete all defined namespace.* names. 368fc3da5bbdcd63840cd224282812c050c5520db11mbligh 369fc3da5bbdcd63840cd224282812c050c5520db11mbligh @param namespace The namespace to be cleared. 370fc3da5bbdcd63840cd224282812c050c5520db11mbligh """ 371fc3da5bbdcd63840cd224282812c050c5520db11mbligh if namespace in self._state: 372fc3da5bbdcd63840cd224282812c050c5520db11mbligh del self._state[namespace] 373fc3da5bbdcd63840cd224282812c050c5520db11mbligh logging.debug('Persistent state %s.* deleted', namespace) 374fc3da5bbdcd63840cd224282812c050c5520db11mbligh 375fc3da5bbdcd63840cd224282812c050c5520db11mbligh 376fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @staticmethod 377fc3da5bbdcd63840cd224282812c050c5520db11mbligh def property_factory(state_attribute, property_attribute, default, 378fc3da5bbdcd63840cd224282812c050c5520db11mbligh namespace='global_properties'): 379fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 380fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh Create a property object for an attribute using self.get and self.set. 381fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 382fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param state_attribute A string with the name of the attribute on 383fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh job that contains the job_state instance. 384fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param property_attribute A string with the name of the attribute 385fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh this property is exposed as. 386fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param default A default value that should be used for this property 387fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh if it is not set. 388fc3da5bbdcd63840cd224282812c050c5520db11mbligh @param namespace The namespace to store the attribute value in. 389fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 390fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @returns A read-write property object that performs self.get calls 391fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh to read the value and self.set calls to set it. 392fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 393fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def getter(job): 394fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh state = getattr(job, state_attribute) 395fc3da5bbdcd63840cd224282812c050c5520db11mbligh return state.get(namespace, property_attribute, default) 396fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def setter(job, value): 397fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh state = getattr(job, state_attribute) 398fc3da5bbdcd63840cd224282812c050c5520db11mbligh state.set(namespace, property_attribute, value) 399fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh return property(getter, setter) 400fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 401fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 402da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanskiclass base_job(object): 403da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """An abstract base class for the various autotest job classes. 404da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 405da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Properties: 406da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski autodir 407da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The top level autotest directory. 408da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski clientdir 409da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The autotest client directory. 410da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski serverdir 411da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The autotest server directory. [OPTIONAL] 412da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski resultdir 413da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The directory where results should be written out. If not specified 414da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski then results should not be written anywhere. [WRITABLE] 415da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 416da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski pkgdir 417da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The job packages directory. [WRITABLE] 418da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski tmpdir 419da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The job temporary directory. [WRITABLE] 420da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski testdir 421da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The job test directory. [WRITABLE] 422da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski site_testdir 423da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The job site test directory. [WRITABLE] 424da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 425da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski bindir 426da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The client bin/ directory. 427da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski configdir 428da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The client config/ directory. 429da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski profdir 430da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The client profilers/ directory. 431da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski toolsdir 432da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The client tools/ directory. 433da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 434da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski conmuxdir 435da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The conmux directory. [OPTIONAL] 436da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 437da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski control 438da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A path to the control file to be executed. [OPTIONAL] 439da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski hosts 440da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A set of all live Host objects currently in use by the job. 441fc3da5bbdcd63840cd224282812c050c5520db11mbligh Code running in the context of a local client can safely assume 442fc3da5bbdcd63840cd224282812c050c5520db11mbligh that this set contains only a single entry. 443da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski machines 444da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A list of the machine names associated with the job. 445da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski user 446da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The user executing the job. 447da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski tag 448da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A tag identifying the job. Often used by the scheduler to give 4499de6ed7ab19c29b5072d52439f434453df16bec0mbligh a name of the form NUMBER-USERNAME/HOSTNAME. 450da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 451da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski last_boot_tag 452fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh The label of the kernel from the last reboot. [OPTIONAL,PERSISTENT] 453fc3da5bbdcd63840cd224282812c050c5520db11mbligh automatic_test_tag 454fc3da5bbdcd63840cd224282812c050c5520db11mbligh A string which, if set, will be automatically added to the test 455fc3da5bbdcd63840cd224282812c050c5520db11mbligh name when running tests. 456fc3da5bbdcd63840cd224282812c050c5520db11mbligh 457da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski default_profile_only 458da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A boolean indicating the default value of profile_only used 459fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh by test.execute. [PERSISTENT] 460da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski drop_caches 461da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A boolean indicating if caches should be dropped before each 462da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski test is executed. 463da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski drop_caches_between_iterations 464da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A boolean indicating if caches should be dropped before each 465da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski test iteration is executed. 466fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh run_test_cleanup 467fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh A boolean indicating if test.cleanup should be run by default 468fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh after a test completes, if the run_cleanup argument is not 469fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh specified. [PERSISTENT] 470da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 471da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski num_tests_run 472da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The number of tests run during the job. [OPTIONAL] 473da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski num_tests_failed 474da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski The number of tests failed during the job. [OPTIONAL] 475da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 476da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski bootloader 477da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski An instance of the boottool class. May not be available on job 478da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski instances where access to the bootloader is not available 479da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski (e.g. on the server running a server job). [OPTIONAL] 480da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski harness 481da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski An instance of the client test harness. Only available in contexts 482da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski where client test execution happens. [OPTIONAL] 483da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski logging 484da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski An instance of the logging manager associated with the job. 485da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski profilers 486da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski An instance of the profiler manager associated with the job. 487da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski sysinfo 488da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski An instance of the sysinfo object. Only available in contexts 489da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski where it's possible to collect sysinfo. 490da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski warning_manager 491da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A class for managing which types of WARN messages should be 492da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski logged and which should be supressed. [OPTIONAL] 493da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski warning_loggers 494da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski A set of readable streams that will be monitored for WARN messages 495da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski to be logged. [OPTIONAL] 496da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 497da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Abstract methods: 498da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski _find_base_directories [CLASSMETHOD] 499da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Returns the location of autodir, clientdir and serverdir 500da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 501da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski _find_resultdir 502da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Returns the location of resultdir. Gets a copy of any parameters 503da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski passed into base_job.__init__. Can return None to indicate that 504da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski no resultdir is to be used. 505da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 506da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 507fc3da5bbdcd63840cd224282812c050c5520db11mbligh # capture the dependency on several helper classes with factories 508fc3da5bbdcd63840cd224282812c050c5520db11mbligh _job_directory = job_directory 509fc3da5bbdcd63840cd224282812c050c5520db11mbligh _job_state = job_state 510fc3da5bbdcd63840cd224282812c050c5520db11mbligh 511fc3da5bbdcd63840cd224282812c050c5520db11mbligh 512da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # all the job directory attributes 513fc3da5bbdcd63840cd224282812c050c5520db11mbligh autodir = _job_directory.property_factory('autodir') 514fc3da5bbdcd63840cd224282812c050c5520db11mbligh clientdir = _job_directory.property_factory('clientdir') 515fc3da5bbdcd63840cd224282812c050c5520db11mbligh serverdir = _job_directory.property_factory('serverdir') 516fc3da5bbdcd63840cd224282812c050c5520db11mbligh resultdir = _job_directory.property_factory('resultdir') 517fc3da5bbdcd63840cd224282812c050c5520db11mbligh pkgdir = _job_directory.property_factory('pkgdir') 518fc3da5bbdcd63840cd224282812c050c5520db11mbligh tmpdir = _job_directory.property_factory('tmpdir') 519fc3da5bbdcd63840cd224282812c050c5520db11mbligh testdir = _job_directory.property_factory('testdir') 520fc3da5bbdcd63840cd224282812c050c5520db11mbligh site_testdir = _job_directory.property_factory('site_testdir') 521fc3da5bbdcd63840cd224282812c050c5520db11mbligh bindir = _job_directory.property_factory('bindir') 522fc3da5bbdcd63840cd224282812c050c5520db11mbligh configdir = _job_directory.property_factory('configdir') 523fc3da5bbdcd63840cd224282812c050c5520db11mbligh profdir = _job_directory.property_factory('profdir') 524fc3da5bbdcd63840cd224282812c050c5520db11mbligh toolsdir = _job_directory.property_factory('toolsdir') 525fc3da5bbdcd63840cd224282812c050c5520db11mbligh conmuxdir = _job_directory.property_factory('conmuxdir') 526fc3da5bbdcd63840cd224282812c050c5520db11mbligh 527fc3da5bbdcd63840cd224282812c050c5520db11mbligh 528fc3da5bbdcd63840cd224282812c050c5520db11mbligh # all the generic persistent properties 5299de6ed7ab19c29b5072d52439f434453df16bec0mbligh tag = _job_state.property_factory('_state', 'tag', '') 530fc3da5bbdcd63840cd224282812c050c5520db11mbligh default_profile_only = _job_state.property_factory( 531fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh '_state', 'default_profile_only', False) 532fc3da5bbdcd63840cd224282812c050c5520db11mbligh run_test_cleanup = _job_state.property_factory( 533fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh '_state', 'run_test_cleanup', True) 534fc3da5bbdcd63840cd224282812c050c5520db11mbligh last_boot_tag = _job_state.property_factory( 535fc3da5bbdcd63840cd224282812c050c5520db11mbligh '_state', 'last_boot_tag', None) 536fc3da5bbdcd63840cd224282812c050c5520db11mbligh automatic_test_tag = _job_state.property_factory( 537fc3da5bbdcd63840cd224282812c050c5520db11mbligh '_state', 'automatic_test_tag', None) 538fc3da5bbdcd63840cd224282812c050c5520db11mbligh 539fc3da5bbdcd63840cd224282812c050c5520db11mbligh # the use_sequence_number property 540fc3da5bbdcd63840cd224282812c050c5520db11mbligh _sequence_number = _job_state.property_factory( 541fc3da5bbdcd63840cd224282812c050c5520db11mbligh '_state', '_sequence_number', None) 542fc3da5bbdcd63840cd224282812c050c5520db11mbligh def _get_use_sequence_number(self): 543fc3da5bbdcd63840cd224282812c050c5520db11mbligh return bool(self._sequence_number) 544fc3da5bbdcd63840cd224282812c050c5520db11mbligh def _set_use_sequence_number(self, value): 545fc3da5bbdcd63840cd224282812c050c5520db11mbligh if value: 546fc3da5bbdcd63840cd224282812c050c5520db11mbligh self._sequence_number = 1 547fc3da5bbdcd63840cd224282812c050c5520db11mbligh else: 548fc3da5bbdcd63840cd224282812c050c5520db11mbligh self._sequence_number = None 549fc3da5bbdcd63840cd224282812c050c5520db11mbligh use_sequence_number = property(_get_use_sequence_number, 550fc3da5bbdcd63840cd224282812c050c5520db11mbligh _set_use_sequence_number) 551da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 552da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 553da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def __init__(self, *args, **dargs): 554da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # initialize the base directories, all others are relative to these 555da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski autodir, clientdir, serverdir = self._find_base_directories() 556da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._autodir = self._job_directory(autodir) 557da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._clientdir = self._job_directory(clientdir) 558da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if serverdir: 559da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._serverdir = self._job_directory(serverdir) 560da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski else: 561da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._serverdir = None 562da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 563da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # initialize all the other directories relative to the base ones 564da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._initialize_dir_properties() 565da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._resultdir = self._job_directory( 566da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._find_resultdir(*args, **dargs), True) 567da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._execution_contexts = [] 568da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 569fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh # initialize all the job state 570fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self._state = self._job_state() 571fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 572da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 573da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @classmethod 574da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def _find_base_directories(cls): 575da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski raise NotImplementedError() 576da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 577da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 578da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def _initialize_dir_properties(self): 579da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 580da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Initializes all the secondary self.*dir properties. Requires autodir, 581da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski clientdir and serverdir to already be initialized. 582da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 583da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # create some stubs for use as shortcuts 584da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def readonly_dir(*args): 585da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski return self._job_directory(os.path.join(*args)) 586da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def readwrite_dir(*args): 587da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski return self._job_directory(os.path.join(*args), True) 588da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 589da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # various client-specific directories 590da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._bindir = readonly_dir(self.clientdir, 'bin') 591da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._configdir = readonly_dir(self.clientdir, 'config') 592da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._profdir = readonly_dir(self.clientdir, 'profilers') 593da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._pkgdir = readwrite_dir(self.clientdir, 'packages') 594da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._toolsdir = readonly_dir(self.clientdir, 'tools') 595da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 596da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # directories which are in serverdir on a server, clientdir on a client 597da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if self.serverdir: 598da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski root = self.serverdir 599da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski else: 600da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski root = self.clientdir 601da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._tmpdir = readwrite_dir(root, 'tmp') 602da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._testdir = readwrite_dir(root, 'tests') 603da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._site_testdir = readwrite_dir(root, 'site_tests') 604da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 605da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski # various server-specific directories 606da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if self.serverdir: 607da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._conmuxdir = readonly_dir(self.autodir, 'conmux') 608da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski else: 609da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._conmuxdir = None 610da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 611da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 612da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def _find_resultdir(self, *args, **dargs): 613da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski raise NotImplementedError() 614da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 615da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 616da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def push_execution_context(self, resultdir): 617da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 618da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Save off the current context of the job and change to the given one. 619da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 620da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski In practice method just changes the resultdir, but it may become more 621da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski extensive in the future. The expected use case is for when a child 622da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski job needs to be executed in some sort of nested context (for example 623da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski the way parallel_simple does). The original context can be restored 624da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski with a pop_execution_context call. 625da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 626da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @param resultdir The new resultdir, relative to the current one. 627da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 628da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski new_dir = self._job_directory( 629da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski os.path.join(self.resultdir, resultdir), True) 630da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._execution_contexts.append(self._resultdir) 631da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._resultdir = new_dir 632da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 633da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 634da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski def pop_execution_context(self): 635da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 636da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski Reverse the effects of the previous push_execution_context call. 637da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski 638da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski @raises IndexError raised when the stack of contexts is empty. 639da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski """ 640da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski if not self._execution_contexts: 641da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski raise IndexError('No old execution context to restore') 642da2f143ff7658eca507041cd2393ead3b68cfa5ejadmanski self._resultdir = self._execution_contexts.pop() 643fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 644fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 645fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def get_state(self, name, default=_job_state.NO_DEFAULT): 646fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Returns the value associated with a particular name. 647fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 648fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param name The name the value was saved with. 649fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param default A default value to return if no state is currently 650fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh associated with var. 651fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 652fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @returns A deep copy of the value associated with name. Note that this 653fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh explicitly returns a deep copy to avoid problems with mutable 654fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh values; mutations are not persisted or shared. 655fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @raises KeyError raised when no state is associated with var and a 656fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh default value is not provided. 657fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 658fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh try: 659fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh return self._state.get('public', name, default=default) 660fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh except KeyError: 661fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh raise KeyError(name) 662fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 663fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 664fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh def set_state(self, name, value): 665fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """Saves the value given with the provided name. 666fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 667fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param name The name the value should be saved with. 668fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh @param value The value to save. 669fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 670fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh self._state.set('public', name, value) 671fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 672fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 673fc3da5bbdcd63840cd224282812c050c5520db11mbligh def _build_tagged_test_name(self, testname, dargs): 674fc3da5bbdcd63840cd224282812c050c5520db11mbligh """Builds the fully tagged testname and subdirectory for job.run_test. 675fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 676fc3da5bbdcd63840cd224282812c050c5520db11mbligh @param testname The base name of the test 677fc3da5bbdcd63840cd224282812c050c5520db11mbligh @param dargs The ** arguments passed to run_test. And arguments 678fc3da5bbdcd63840cd224282812c050c5520db11mbligh consumed by this method will be removed from the dictionary. 679fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh 680fc3da5bbdcd63840cd224282812c050c5520db11mbligh @returns A 3-tuple of the full name of the test, the subdirectory it 681fc3da5bbdcd63840cd224282812c050c5520db11mbligh should be stored in, and the full tag of the subdir. 682fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 683fc3da5bbdcd63840cd224282812c050c5520db11mbligh tag_parts = [] 684fc3da5bbdcd63840cd224282812c050c5520db11mbligh 685fc3da5bbdcd63840cd224282812c050c5520db11mbligh # build up the parts of the tag used for the test name 686fc3da5bbdcd63840cd224282812c050c5520db11mbligh base_tag = dargs.pop('tag', None) 687fc3da5bbdcd63840cd224282812c050c5520db11mbligh if base_tag: 688fc3da5bbdcd63840cd224282812c050c5520db11mbligh tag_parts.append(str(base_tag)) 689fc3da5bbdcd63840cd224282812c050c5520db11mbligh if self.use_sequence_number: 690fc3da5bbdcd63840cd224282812c050c5520db11mbligh tag_parts.append('_%02d_' % self._sequence_number) 691fc3da5bbdcd63840cd224282812c050c5520db11mbligh self._sequence_number += 1 692fc3da5bbdcd63840cd224282812c050c5520db11mbligh if self.automatic_test_tag: 693fc3da5bbdcd63840cd224282812c050c5520db11mbligh tag_parts.append(self.automatic_test_tag) 694fc3da5bbdcd63840cd224282812c050c5520db11mbligh full_testname = '.'.join([testname] + tag_parts) 695fc3da5bbdcd63840cd224282812c050c5520db11mbligh 696fc3da5bbdcd63840cd224282812c050c5520db11mbligh # build up the subdir and tag as well 697fc3da5bbdcd63840cd224282812c050c5520db11mbligh subdir_tag = dargs.pop('subdir_tag', None) 698fc3da5bbdcd63840cd224282812c050c5520db11mbligh if subdir_tag: 699fc3da5bbdcd63840cd224282812c050c5520db11mbligh tag_parts.append(subdir_tag) 700fc3da5bbdcd63840cd224282812c050c5520db11mbligh subdir = '.'.join([testname] + tag_parts) 701fc3da5bbdcd63840cd224282812c050c5520db11mbligh tag = '.'.join(tag_parts) 702fc3da5bbdcd63840cd224282812c050c5520db11mbligh 703fc3da5bbdcd63840cd224282812c050c5520db11mbligh return full_testname, subdir, tag 704fc3da5bbdcd63840cd224282812c050c5520db11mbligh 705fc3da5bbdcd63840cd224282812c050c5520db11mbligh 706fc3da5bbdcd63840cd224282812c050c5520db11mbligh def _make_test_outputdir(self, subdir): 707fc3da5bbdcd63840cd224282812c050c5520db11mbligh """Creates an output directory for a test to run it. 708fc3da5bbdcd63840cd224282812c050c5520db11mbligh 709fc3da5bbdcd63840cd224282812c050c5520db11mbligh @param subdir The subdirectory of the test. Generally computed by 710fc3da5bbdcd63840cd224282812c050c5520db11mbligh _build_tagged_test_name. 711fc3da5bbdcd63840cd224282812c050c5520db11mbligh 712fc3da5bbdcd63840cd224282812c050c5520db11mbligh @returns A job_directory instance corresponding to the outputdir of 713fc3da5bbdcd63840cd224282812c050c5520db11mbligh the test. 714fc3da5bbdcd63840cd224282812c050c5520db11mbligh @raises A TestError if the output directory is invalid. 715fbf73aecdd357094ae05e7d1e4ea99b1ecf93ee4mbligh """ 716fc3da5bbdcd63840cd224282812c050c5520db11mbligh # explicitly check that this subdirectory is new 717fc3da5bbdcd63840cd224282812c050c5520db11mbligh path = os.path.join(self.resultdir, subdir) 718fc3da5bbdcd63840cd224282812c050c5520db11mbligh if os.path.exists(path): 719fc3da5bbdcd63840cd224282812c050c5520db11mbligh msg = ('%s already exists; multiple tests cannot run with the ' 720fc3da5bbdcd63840cd224282812c050c5520db11mbligh 'same subdirectory' % subdir) 721fc3da5bbdcd63840cd224282812c050c5520db11mbligh raise error.TestError(msg) 722fc3da5bbdcd63840cd224282812c050c5520db11mbligh 723fc3da5bbdcd63840cd224282812c050c5520db11mbligh # create the outputdir and raise a TestError if it isn't valid 724fc3da5bbdcd63840cd224282812c050c5520db11mbligh try: 725fc3da5bbdcd63840cd224282812c050c5520db11mbligh outputdir = self._job_directory(path, True) 726fc3da5bbdcd63840cd224282812c050c5520db11mbligh return outputdir 727fc3da5bbdcd63840cd224282812c050c5520db11mbligh except self._job_directory.JobDirectoryException, e: 728fc3da5bbdcd63840cd224282812c050c5520db11mbligh logging.exception('%s directory creation failed with %s', 729fc3da5bbdcd63840cd224282812c050c5520db11mbligh subdir, e) 730fc3da5bbdcd63840cd224282812c050c5520db11mbligh raise error.TestError('%s directory creation failed' % subdir) 731