Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ lite-test:

# To run the server, for convenience
run:
BLOSC_TRACE=1 ${BIN}/python3 -m caterva2.services.sub
${BIN}/python3 -m caterva2.services.sub
14 changes: 1 addition & 13 deletions SPECS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,8 @@ The client must implement the following commands:
There should be a configuration file (by default $CWD/caterva2.toml) where the configuration for each service is specified. For example:

```
[broker]
[subscriber]
http = "localhost:8000"
statedir = "_caterva2/bro"
loglevel = "warning"

[publisher.1]
http = "localhost:8001"
statedir = "_caterva2/pub"
loglevel = "warning"
name = "foo"
root = "root-examples"

[subscriber.1]
http = "localhost:8002"
urlbase = "https://cat2.example.com" # e.g. served by reverse proxy
statedir = "_caterva2/sub"
loglevel = "warning"
Expand Down
8 changes: 4 additions & 4 deletions caterva2.sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
# The subscriber section must define:
#
# - statedir: the directory where the subcriber's data will be stored (default: _caterva2/sub)
# - http: where the subscriber listens to (a unix socket or a host/port) (default: localhost:8002)
# - urlbase: the base url users will use to reach the subscriber (default: http://localhost:8002)
# - http: where the subscriber listens to (a unix socket or a host/port) (default: localhost:8000)
# - urlbase: the base url users will use to reach the subscriber (default: http://localhost:8000)
# - quota: if defined, it will limit the disk usage (default: 0, no limit)
# - maxusers: if defined, it will limit the number of users (default: 0, no limit)
# - login: if true, users will need to authenticate (default: true)
Expand All @@ -16,8 +16,8 @@
[subscriber]
statedir = "_caterva2/sub"
#http = "_caterva2/sub/uvicorn.socket"
http = "localhost:8002"
urlbase = "http://localhost:8002"
http = "localhost:8000"
urlbase = "http://localhost:8000"
quota = "10G"
maxusers = 5
register = true # allow users to register
Expand Down
2 changes: 1 addition & 1 deletion caterva2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from . import api_utils, utils

sub_urlbase_default = "http://localhost:8002"
sub_urlbase_default = "http://localhost:8000"
"""The default base of URLs provided by the subscriber."""


Expand Down
1 change: 1 addition & 0 deletions caterva2/clients/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def cmd_listusers(client, args):


def main():
# Build the parser
conf = utils.get_conf()
parser = utils.get_parser()
parser.add_argument(
Expand Down
25 changes: 13 additions & 12 deletions caterva2/clients/tbrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@


class TreeApp(App):

def __init__(self, args):
super().__init__()
self.root = args.root
Expand All @@ -28,8 +27,7 @@ def __init__(self, args):
user_auth = {"username": args.username, "password": args.password}
auth_cookie = api_utils.get_auth_cookie(args.urlbase, user_auth)
api.subscribe(args.root, args.urlbase, auth_cookie=auth_cookie)
self.data = api.get_list(args.root, args.urlbase,
auth_cookie=auth_cookie)
self.data = api.get_list(args.root, args.urlbase, auth_cookie=auth_cookie)

def compose(self) -> ComposeResult:
path = self.root / pathlib.Path(self.data[0])
Expand All @@ -49,18 +47,21 @@ def compose(self) -> ComposeResult:


def main():
# Load configuration (args)
conf = utils.get_conf()
parser = utils.get_parser()
parser.add_argument('--subscriber',
dest='urlbase', type=utils.urlbase_type,
default=conf.get('subscriber.url',
api.sub_urlbase_default))
parser.add_argument('--username', default=conf.get('client.username'))
parser.add_argument('--password', default=conf.get('client.password'))
parser.add_argument('--root', default='foo')

# Go
parser.add_argument(
"--subscriber",
dest="urlbase",
type=utils.urlbase_type,
default=conf.get("subscriber.url", api.sub_urlbase_default),
)
parser.add_argument("--username", default=conf.get("client.username"))
parser.add_argument("--password", default=conf.get("client.password"))
parser.add_argument("--root", default="foo")
args = utils.run_parser(parser)

# Start client
app = TreeApp(args)
app.run()

Expand Down
6 changes: 1 addition & 5 deletions caterva2/services/plugins/tomography/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,11 @@
contenttype = "tomography"


abspath_and_dataprep = None
urlbase = None


def init(absp_n_datap, urlbase_):
global abspath_and_dataprep
def init(urlbase_):
global urlbase
abspath_and_dataprep = absp_n_datap
urlbase = urlbase_


Expand Down Expand Up @@ -101,7 +98,6 @@ async def display(


async def __get_image(path, user, ndim, i):
# Alternatively, call abspath_and_dataprep with the corresponding slice to download data async
array = await get_container(path, user)
index = [slice(None) for x in array.shape]
index[ndim] = slice(i, i + 1, 1)
Expand Down
5 changes: 2 additions & 3 deletions caterva2/services/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ def parse_size(size):
return int(number * units[unit])


conf = utils.get_conf("subscriber", allow_id=True)
conf = utils.get_conf("subscriber")

urlbase = conf.get(".urlbase", "http://localhost:8002")
urlbase = conf.get(".urlbase", "http://localhost:8000")
login = conf.get(".login", True)
register = conf.get(".register", False)
demo = conf.get(".demo", False)
Expand All @@ -41,7 +41,6 @@ def parse_size(size):
# Not strictly necessary but useful for documentation
statedir = None
database = None # <Database> instance
cache = None
personal = None
shared = None
public = None
101 changes: 21 additions & 80 deletions caterva2/services/srv_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import blosc2
import fastapi
import safer
import uvicorn
from fastapi_users.exceptions import UserNotExists
from sqlalchemy.future import select

Expand All @@ -47,24 +46,6 @@ def compress_file(path):
path.unlink()


def cache_lookup(cachedir, path, may_not_exist=False):
if cachedir == path:
# Special case for the cache root
return path
path = pathlib.Path(path)
if (cachedir / path).is_dir():
# Special case for directories
return cachedir / path

# HDF5 files cannot be compressed, as they are supported natively
if path.suffix not in {".b2frame", ".b2nd", ".h5"} and not may_not_exist:
if path.is_file():
compress_file(path)
path = f"{path}.b2"

return get_abspath(cachedir, path, may_not_exist)


def get_model_from_obj(obj, model_class, **kwargs):
if isinstance(obj, dict):

Expand Down Expand Up @@ -103,7 +84,7 @@ def getter(o, k):
return model_class(**data)


def read_metadata(obj, cache=None, personal=None, shared=None, public=None):
def read_metadata(obj):
# Open dataset
if isinstance(obj, pathlib.Path):
path = obj
Expand Down Expand Up @@ -154,10 +135,6 @@ def read_metadata(obj, cache=None, personal=None, shared=None, public=None):
# overwrite operands and expression with _tosave versions for metadata display
operands = operands_as_paths(
obj.operands_tosave if hasattr(obj, "operands_tosave") else obj.operands,
cache,
personal,
shared,
public,
)
return get_model_from_obj(
obj,
Expand All @@ -184,47 +161,33 @@ def reformat_cparams(cparams):
return cparams


def get_relpath(path, cache=None, personal=None, shared=None, public=None):
if cache is None:
cache = settings.cache
if personal is None:
personal = settings.personal
if shared is None:
shared = settings.shared
if public is None:
public = settings.public

def get_relpath(path):
if not isinstance(path, pathlib.Path):
path = pathlib.Path(path.schunk.urlpath)

# Public: /.../<public>/<subpath> to <path> (i.e. no change)
public = settings.public
if public is not None and path.is_relative_to(public):
path = path.relative_to(public)
parts = ["@public"] + list(path.parts)
return pathlib.Path(*parts)

# Shared: /.../<shared>/<subpath> to <path> (i.e. no change)
shared = settings.shared
if shared is not None and path.is_relative_to(shared):
# Shared: /.../<shared>/<subpath> to <path> (i.e. no change)
path = path.relative_to(shared)
parts = ["@shared"] + list(path.parts)
return pathlib.Path(*parts)
elif public is not None and path.is_relative_to(public):
# Shared: /.../<public>/<subpath> to <path> (i.e. no change)
path = path.relative_to(public)
parts = ["@public"] + list(path.parts)
return pathlib.Path(*parts)
try:
# Cache: /.../<root>/<subpath> to <root>/<subpath>
path = path.relative_to(cache)
except ValueError:
# personal: /.../<uid>/<subpath> to @personal/<subpath>
path = path.relative_to(personal)
parts = list(path.parts)
parts[0] = "@personal"
path = pathlib.Path(*parts)

return path


def operands_as_paths(operands, cache, personal, shared, public):
return {
nm: None if op is None else str(get_relpath(op, cache, personal, shared, public))
for (nm, op) in operands.items()
}

# Personal: /.../<uid>/<subpath> to @personal/<subpath>
path = path.relative_to(settings.personal)
parts = list(path.parts)
parts[0] = "@personal"
return pathlib.Path(*parts)


def operands_as_paths(operands):
return {nm: None if op is None else str(get_relpath(op)) for (nm, op) in operands.items()}


#
Expand All @@ -246,28 +209,6 @@ def raise_not_found(detail="Not Found"):
raise fastapi.HTTPException(status_code=404, detail=detail)


def get_abspath(root, path, may_not_exist=False):
abspath = root / path

# Security check
if root not in abspath.parents:
raise_bad_request(f"Invalid path {path}")

# Existence check
if not abspath.is_file() and not may_not_exist:
raise_not_found()

return abspath


def uvicorn_run(app, args, root_path=""):
http = args.http
if http.uds:
uvicorn.run(app, uds=http.uds, root_path=root_path)
else:
uvicorn.run(app, host=http.host, port=http.port, root_path=root_path)


#
# Blosc2 related helpers
#
Expand Down
Loading