#
# Copyright (c) 2015, ParaTools, Inc.
# All rights reserved.
#
# 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.
# (3) Neither the name of ParaTools, Inc. nor the names of its contributors may
# be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
#
"""TAU project storage container
Project-level records define the project and its member components. The user may also
want to install software packages at the project level to avoid quotas or in situations
where :any:`USER_PREFIX` is not accessible from cluster compute nodes.
"""
import os
from taucmdr import logger, util
from taucmdr import PROJECT_DIR
from taucmdr.cf.storage import StorageError
from taucmdr.cf.storage.local_file import LocalFileStorage
LOGGER = logger.get_logger(__name__)
[docs]class ProjectStorageError(StorageError):
"""Indicates that the project storage has not been initialized."""
message_fmt = ("%(value)s\n"
"\n"
"%(hints)s\n"
"Please contact %(contact)s for assistance.")
def __init__(self, search_root):
"""Initialize the error object.
Args:
search_root (str): Directory in which the search for a project directory was initiated.
"""
value = "Project directory not found in '%s' or any of its parent directories." % search_root
hints = ("Make sure that you have already run the `tau initialize` command "
"in this directory or any of its parent directories.")
super().__init__(value, hints)
self.search_root = search_root
[docs]class ProjectStorage(LocalFileStorage):
"""Handle the special case project storage.
Each TAU Commander project has its own project storage that holds project-specific files
(i.e. performance data) and the project configuration.
"""
def __init__(self):
super().__init__('project', None)
self._force_cwd = False
self._tau_directory = None
[docs] def connect_filesystem(self, *args, **kwargs):
"""Prepares the store filesystem for reading and writing."""
from taucmdr.cf.storage.levels import USER_STORAGE
try:
project_prefix = self.prefix
except ProjectStorageError as err:
project_prefix = os.path.join(os.getcwd(), PROJECT_DIR)
if os.path.exists(os.path.join(project_prefix, USER_STORAGE.name + ".json")):
raise StorageError("Cannot create project in home directory. "
"Use '-@ user' option for user level storage.") from err
try:
util.mkdirp(project_prefix)
except Exception as err:
raise StorageError("Failed to access %s filesystem prefix '%s': %s" %
(self.name, project_prefix, err)) from err
# Exclude project storage directory from git
with open(os.path.join(self.prefix, '.gitignore'), 'w+') as fout:
fout.write('/*\n')
LOGGER.debug("Initialized %s filesystem prefix '%s'", self.name, project_prefix)
[docs] def destroy(self, *args, **kwargs):
"""Disconnects the database and filesystem and recursively deletes the filesystem.
Args:
*args: Passed through to :any:`disconnect_filesystem`.
**kwargs: Keyword arguments for :any:`disconnect_filesystem` or :any:`shutil.rmtree`.
"""
self.disconnect_filesystem(*args, **kwargs)
ignore_errors = kwargs.pop('ignore_errors', False)
onerror = kwargs.pop('onerror', None)
if self._prefix:
util.rmtree(self._prefix, ignore_errors=ignore_errors, onerror=onerror)
self._prefix = None
@property
def prefix(self):
"""Searches the current directory and its parents for a TAU Commander project directory.
This method **does not** create or modify files. If the project directory cannot be found
then an error is raised. It's up to the caller to determine how the error should be handled.
Returns:
str: The project directory, i.e. this storage container's filesystem prefix.
Raises:
ProjectStorageError: Neither the current directory nor any of its parent directories contain
a TAU Commander project directory.
"""
from taucmdr.cf.storage.levels import USER_STORAGE, SYSTEM_STORAGE
if self._prefix:
return self._prefix
cwd = os.getcwd()
if self._force_cwd:
# Only check current working directory for project directory
prefix = os.path.realpath(os.path.join(cwd, PROJECT_DIR))
if os.path.isdir(prefix):
for exclude_storage in USER_STORAGE, SYSTEM_STORAGE:
if os.path.exists(os.path.join(prefix, exclude_storage.name + ".json")):
break
else:
LOGGER.debug("Located project storage prefix '%s'", prefix)
self._prefix = prefix
return prefix
raise ProjectStorageError(cwd)
LOGGER.debug("Searching upwards from '%s' for '%s'", cwd, PROJECT_DIR)
if self._tau_directory:
root = os.path.realpath(self._tau_directory)
else:
root = cwd
lastroot = None
while root and root != lastroot:
prefix = os.path.realpath(os.path.join(root, PROJECT_DIR))
if os.path.isdir(prefix):
for exclude_storage in USER_STORAGE, SYSTEM_STORAGE:
if os.path.exists(os.path.join(prefix, exclude_storage.name + ".json")):
break
else:
LOGGER.debug("Located project storage prefix '%s'", prefix)
self._prefix = prefix
return prefix
lastroot = root
root = os.path.dirname(root)
raise ProjectStorageError(cwd)
[docs] def force_cwd(self, force):
self._force_cwd = force
[docs] def tau_dir(self, taudir):
self._tau_directory = taudir