hsds 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- admin/__init__.py +0 -0
- admin/config/config.yml +106 -0
- hsds/__init__.py +0 -0
- hsds/app.py +385 -0
- hsds/async_lib.py +536 -0
- hsds/attr_dn.py +619 -0
- hsds/attr_sn.py +1436 -0
- hsds/basenode.py +711 -0
- hsds/chunk_crawl.py +915 -0
- hsds/chunk_dn.py +889 -0
- hsds/chunk_sn.py +1482 -0
- hsds/chunklocator.py +234 -0
- hsds/config.py +189 -0
- hsds/ctype_dn.py +196 -0
- hsds/ctype_sn.py +302 -0
- hsds/datanode.py +446 -0
- hsds/datanode_lib.py +1385 -0
- hsds/domain_crawl.py +567 -0
- hsds/domain_dn.py +228 -0
- hsds/domain_sn.py +1603 -0
- hsds/dset_dn.py +308 -0
- hsds/dset_lib.py +1047 -0
- hsds/dset_sn.py +1202 -0
- hsds/folder_crawl.py +131 -0
- hsds/group_dn.py +337 -0
- hsds/group_sn.py +295 -0
- hsds/headnode.py +516 -0
- hsds/hsds_app.py +402 -0
- hsds/hsds_logger.py +207 -0
- hsds/link_dn.py +486 -0
- hsds/link_sn.py +774 -0
- hsds/node_runner.py +53 -0
- hsds/servicenode.py +328 -0
- hsds/servicenode_lib.py +1205 -0
- hsds/util/__init__.py +13 -0
- hsds/util/arrayUtil.py +731 -0
- hsds/util/attrUtil.py +76 -0
- hsds/util/authUtil.py +663 -0
- hsds/util/awsLambdaClient.py +195 -0
- hsds/util/azureBlobClient.py +521 -0
- hsds/util/boolparser.py +293 -0
- hsds/util/chunkUtil.py +1551 -0
- hsds/util/domainUtil.py +325 -0
- hsds/util/dsetUtil.py +1063 -0
- hsds/util/fileClient.py +427 -0
- hsds/util/globparser.py +129 -0
- hsds/util/hdf5dtype.py +869 -0
- hsds/util/httpUtil.py +737 -0
- hsds/util/idUtil.py +540 -0
- hsds/util/jwtUtil.py +210 -0
- hsds/util/k8sClient.py +228 -0
- hsds/util/linkUtil.py +134 -0
- hsds/util/lruCache.py +404 -0
- hsds/util/query_marathon.py +114 -0
- hsds/util/rangegetUtil.py +159 -0
- hsds/util/s3Client.py +694 -0
- hsds/util/storUtil.py +706 -0
- hsds/util/timeUtil.py +83 -0
- hsds-0.9.1.dist-info/LICENSE +208 -0
- hsds-0.9.1.dist-info/METADATA +57 -0
- hsds-0.9.1.dist-info/RECORD +64 -0
- hsds-0.9.1.dist-info/WHEEL +5 -0
- hsds-0.9.1.dist-info/entry_points.txt +7 -0
- hsds-0.9.1.dist-info/top_level.txt +2 -0
admin/__init__.py
ADDED
|
File without changes
|
admin/config/config.yml
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# HSDS configuration
|
|
2
|
+
allow_noauth: true # enable unauthenticated requests
|
|
3
|
+
auth_expiration: -1 # set an expiration for credential caching
|
|
4
|
+
default_public: False # new domains are publically readable by default
|
|
5
|
+
aws_access_key_id: xxx # Replace with access key for account or use aws_iam_role
|
|
6
|
+
aws_secret_access_key: xxx # Replace with secret key for account
|
|
7
|
+
aws_iam_role: hsds_role # For EC2 using IAM roles
|
|
8
|
+
aws_region: us-east-1
|
|
9
|
+
hsds_endpoint: http://hsds.hdf.test # used for hateos links in response
|
|
10
|
+
aws_s3_gateway: null # use endpoint for the region HSDS is running in, e.g. 'https://s3.amazonaws.com' for us-east-1
|
|
11
|
+
aws_s3_no_sign_request: false # do not use credentials for S3 requests, equivalent of --no-sign-request for AWS CLI
|
|
12
|
+
aws_dynamodb_gateway: null # use for dynamodb endpint, e.g. 'https://dynamodb.us-east-1.amazonaws.com',
|
|
13
|
+
aws_dynamodb_users_table: null # set to table name if dynamodb is used to store usernames and passwords
|
|
14
|
+
azure_connection_string: null # use for connecting to Azure blob storage
|
|
15
|
+
azure_resource_id: null # resource id for use with Azure Active Directory
|
|
16
|
+
azure_storage_account: null # storage account to use on Azure
|
|
17
|
+
azure_resource_group: null # Azure resource group the container (BUCKET_NAME) belongs to
|
|
18
|
+
root_dir: null # base directory to use for Posix storage
|
|
19
|
+
password_salt: null # salt value to generate password based on username. Not recommended for public deployments
|
|
20
|
+
bucket_name: hsdstest # set to use a default bucket, otherwise bucket param is needed for all requests
|
|
21
|
+
head_port: 5100 # port to use for head node
|
|
22
|
+
head_ram: 512m # memory for head container
|
|
23
|
+
dn_port: 6101 # Start dn ports at 6101
|
|
24
|
+
dn_ram: 3g # memory for DN container (per container)
|
|
25
|
+
sn_port: 5101 # Start sn ports at 5101
|
|
26
|
+
sn_ram: 3g # memory for SN container
|
|
27
|
+
target_sn_count: 0 # desired number of SN containers
|
|
28
|
+
target_dn_count: 0 # desire number of DN containers
|
|
29
|
+
log_level: INFO # log level. One of ERROR, WARNING, INFO, DEBUG
|
|
30
|
+
log_timestamps: false # emit timestamp with log messages
|
|
31
|
+
log_prefix: null # Prefix text to append to log entries
|
|
32
|
+
max_tcp_connections: 100 # max number of inflight tcp connections
|
|
33
|
+
head_sleep_time: 10 # max sleep time between health checks for head node
|
|
34
|
+
node_sleep_time: 10 # max sleep time between health checks for SN/DN nodes
|
|
35
|
+
async_sleep_time: 1 # max sleep time between async task runs
|
|
36
|
+
scan_sleep_time: 10 # max sleep time between scanning runs
|
|
37
|
+
scan_wait_time: 10 # min time to wait after a domain update before starting a scan
|
|
38
|
+
max_scan_duration: 180 # max time to wait for a scan to complete before raising error
|
|
39
|
+
gc_sleep_time: 10 # max time between runs to delete unused objects
|
|
40
|
+
s3_sync_interval: 1 # time to wait between s3_sync checks (in sec)
|
|
41
|
+
s3_age_time: 1 # time to wait since last update to write an object to S3
|
|
42
|
+
s3_sync_task_timeout: 10 # time to cancel write task if no response
|
|
43
|
+
store_read_timeout: 1 # time to cancel storage read request if no response
|
|
44
|
+
store_read_sleep_interval: 0.1 # time to sleep between checking on read request
|
|
45
|
+
max_pending_write_requests: 20 # maxium number of inflight write requests
|
|
46
|
+
flush_sleep_interval: 1 # time to wait between checking on dirty objects
|
|
47
|
+
flush_timeout: 10 # max time to wait on all I/O operations to complete for a flush
|
|
48
|
+
min_chunk_size: 1m # 1 MB
|
|
49
|
+
max_chunk_size: 4m # 4 MB
|
|
50
|
+
max_request_size: 100m # 100 MB - should be no smaller than client_max_body_size in nginx tmpl (if using nginx)
|
|
51
|
+
max_chunks_per_folder: 0 # max number of chunks per s3 folder. 0 for unlimiited
|
|
52
|
+
max_task_count: 100 # maximum number of concurrent tasks per node before server will return 503 error
|
|
53
|
+
max_tasks_per_node_per_request: 16 # maximum number of inflight tasks to each node per request
|
|
54
|
+
aio_max_pool_connections: 64 # number of connections to keep in conection pool for aiobotocore requests
|
|
55
|
+
client_pool_count: 10 # pool count for SessionClient
|
|
56
|
+
metadata_mem_cache_size: 128m # 128 MB - metadata cache size per DN node
|
|
57
|
+
metadata_mem_cache_expire: 3600 # expire cache items after one hour
|
|
58
|
+
chunk_mem_cache_size: 128m # 128 MB - chunk cache size per DN node
|
|
59
|
+
chunk_mem_cache_expire: 3600 # expire cache items after one hour
|
|
60
|
+
timeout: 30 # http timeout - 30 sec
|
|
61
|
+
password_file: /config/passwd.txt # filepath to a text file of username/passwords. set to '' for no-auth access
|
|
62
|
+
groups_file: /config/groups.txt # filepath to text file defining user groups
|
|
63
|
+
server_name: Highly Scalable Data Service (HSDS) # this gets returned in the about request
|
|
64
|
+
greeting: Welcome to HSDS!
|
|
65
|
+
about: HSDS is a webservice for HDF data
|
|
66
|
+
top_level_domains: [] # list of possible top-level domains, example: ["/home", "/shared"], if empty all top-level folders in default bucket will be returned
|
|
67
|
+
cors_domain: "*" # domains allowed for CORS
|
|
68
|
+
admin_user: admin # user with admin privileges
|
|
69
|
+
admin_group: null # enable admin privileges for any user in this group
|
|
70
|
+
openid_provider: azure # OpenID authentication provider
|
|
71
|
+
openid_url: null # OpenID connect endpoint if provider is not azure or google
|
|
72
|
+
openid_audience: null # OpenID audience. This is synonymous with azure_resource_id for azure
|
|
73
|
+
openid_claims: unique_name,appid,roles # Comma seperated list of claims to resolve to usernames.
|
|
74
|
+
chaos_die: 0 # if > 0, have nodes randomly die after n seconds (for testing)
|
|
75
|
+
standalone_app: false # True when run as a single application
|
|
76
|
+
blosc_nthreads: 2 # number of threads to use for blosc compression. Set to 0 to have blosc auto-determine thread count
|
|
77
|
+
http_compression: false # Use HTTP compression
|
|
78
|
+
http_max_url_length: 512 # Limit http request url + params to be less than this
|
|
79
|
+
http_streaming: true # enable HTTP streaming
|
|
80
|
+
k8s_dn_label_selector: app=hsds # Selector for getting data node pods from a k8s deployment (https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors)
|
|
81
|
+
k8s_namespace: null # Specifies if a the client should be limited to a specific namespace. Useful for some RBAC configurations.
|
|
82
|
+
restart_policy: on-failure # Docker restart policy
|
|
83
|
+
# the following two values with give backoff times of approx: 0.2, 0.4, 0.8, 1.6, 3.2, 6.4, 12.8
|
|
84
|
+
dn_max_retries: 7 # number of time to retry DN requests
|
|
85
|
+
dn_retry_backoff_exp: 0.1 # backoff factor for retries
|
|
86
|
+
xss_protection: "1; mode=block" # Include in response headers if set
|
|
87
|
+
allow_any_bucket_read: true # enable reads to buckets other than default bucket
|
|
88
|
+
allow_any_bucket_write: true # enable writes to buckets other than default bucket
|
|
89
|
+
bit_shuffle_default_blocksize: 2048 # default blocksize for bitshuffle filter
|
|
90
|
+
max_rangeget_gap: 1024 # max gap in byte for intelligent range get requests
|
|
91
|
+
# DEPRECATED - the remaining config values are not used in currently but kept for backward compatibility with older container images
|
|
92
|
+
aws_lambda_chunkread_function: null # name of aws lambda function for chunk reading
|
|
93
|
+
aws_lambda_threshold: 4 # number of chunks per node per request to reach before using lambda
|
|
94
|
+
aws_lambda_max_invoke: 1000 # max number of lambda functions to invoke simultaneously
|
|
95
|
+
aws_lambda_gateway: null # use lambda endpoint for region HSDS is running in
|
|
96
|
+
k8s_app_label: null # The app label for k8s deployments (use k8s_dn_label_selector instead)
|
|
97
|
+
write_zero_chunks: False # write chunk to storage even when it's all zeros (or in general equal to the fill value)
|
|
98
|
+
max_chunks_per_request: 1000 # maximum number of chunks to be serviced by one request
|
|
99
|
+
rangeget_port: 6900 # singleton proxy at port 6900
|
|
100
|
+
rangeget_ram: 2g # memory for RANGEGET container
|
|
101
|
+
data_cache_size: 128m # cache for rangegets
|
|
102
|
+
data_cache_max_req_size: 128k # max size for rangeget fetches
|
|
103
|
+
data_cache_expire_time: 3600 # expire cache items after one hour
|
|
104
|
+
data_cache_page_size: 4m # page size for range get cache, set to zero to disable proxy
|
|
105
|
+
data_cache_max_concurrent_read: 16 # maximum number of inflight storage read requests
|
|
106
|
+
domain_req_max_objects_limit: 500 # maximum number of objects to return in GET domain request with use_cache
|
hsds/__init__.py
ADDED
|
File without changes
|
hsds/app.py
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
##############################################################################
|
|
2
|
+
# Copyright by The HDF Group. #
|
|
3
|
+
# All rights reserved. #
|
|
4
|
+
# #
|
|
5
|
+
# This file is part of HSDS (HDF5 Scalable Data Service), Libraries and #
|
|
6
|
+
# Utilities. The full HSDS copyright notice, including #
|
|
7
|
+
# terms governing use, modification, and redistribution, is contained in #
|
|
8
|
+
# the file COPYING, which can be found at the root of the source code #
|
|
9
|
+
# distribution tree. If you do not have access to this file, you may #
|
|
10
|
+
# request a copy from help@hdfgroup.org. #
|
|
11
|
+
##############################################################################
|
|
12
|
+
import argparse
|
|
13
|
+
import os
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
import logging
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
from .hsds_app import HsdsApp
|
|
20
|
+
from . import config
|
|
21
|
+
|
|
22
|
+
_HELP_USAGE = "Starts HSDS, a REST-based service for HDF5 data."
|
|
23
|
+
|
|
24
|
+
_HELP_EPILOG = """Examples:
|
|
25
|
+
|
|
26
|
+
- with a POSIX-based storage using a directory: ./hsdata for storage:
|
|
27
|
+
|
|
28
|
+
hsds --root_dir ~/hsdata
|
|
29
|
+
|
|
30
|
+
- with POSIX-based storage and config settings and password file:
|
|
31
|
+
|
|
32
|
+
hsds --root_dir ~/hsdata --password_file ./admin/config/passwd.txt \
|
|
33
|
+
--config_dir ./admin/config
|
|
34
|
+
|
|
35
|
+
- with minio data storage:
|
|
36
|
+
|
|
37
|
+
hsds --s3-gateway http://localhost:6007 --access-key-id demo:demo
|
|
38
|
+
--secret-access-key DEMO_PASS --password_file ./admin/config/passwd.txt
|
|
39
|
+
|
|
40
|
+
- with AWS S3 storage and a bucket in the us-west-2 region:
|
|
41
|
+
|
|
42
|
+
hsds --s3-gateway http://s3.us-west-2.amazonaws.com --access-key-id ${AWS_ACCESS_KEY_ID} \
|
|
43
|
+
--secret-access-key ${AWS_SECRET_ACCESS_KEY} --password_file ./admin/config/passwd.txt
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# maximum number of characters if socket directory is given
|
|
48
|
+
# Exceeding this can cause errors - see: https://github.com/HDFGroup/hsds/issues/129
|
|
49
|
+
# Underlying issue is reported here: https://bugs.python.org/issue32958
|
|
50
|
+
MAX_SOCKET_DIR_PATH_LEN = 63
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UserConfig:
|
|
54
|
+
"""
|
|
55
|
+
User Config state
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, config_file=None, **kwargs):
|
|
59
|
+
self._cfg = {}
|
|
60
|
+
if config_file:
|
|
61
|
+
self._config_file = config_file
|
|
62
|
+
elif os.path.isfile(".hscfg"):
|
|
63
|
+
self._config_file = ".hscfg"
|
|
64
|
+
else:
|
|
65
|
+
self._config_file = os.path.expanduser("~/.hscfg")
|
|
66
|
+
# process config file if found
|
|
67
|
+
if os.path.isfile(self._config_file):
|
|
68
|
+
line_number = 0
|
|
69
|
+
with open(self._config_file) as f:
|
|
70
|
+
for line in f:
|
|
71
|
+
line_number += 1
|
|
72
|
+
s = line.strip()
|
|
73
|
+
if not s:
|
|
74
|
+
continue
|
|
75
|
+
if s[0] == "#":
|
|
76
|
+
# comment line
|
|
77
|
+
continue
|
|
78
|
+
index = line.find("=")
|
|
79
|
+
if index <= 0:
|
|
80
|
+
print(
|
|
81
|
+
"config file: {} line: {} is not valid".format(
|
|
82
|
+
self._config_file, line_number
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
continue
|
|
86
|
+
k = line[:index].strip()
|
|
87
|
+
nlen = index + 1
|
|
88
|
+
v = line[nlen:].strip()
|
|
89
|
+
if v and v.upper() != "NONE":
|
|
90
|
+
self._cfg[k] = v
|
|
91
|
+
# override any config values with environment variable if found
|
|
92
|
+
for k in self._cfg.keys():
|
|
93
|
+
if k.upper() in os.environ:
|
|
94
|
+
self._cfg[k] = os.environ[k.upper()]
|
|
95
|
+
|
|
96
|
+
# finally update any values that are passed in to the constructor
|
|
97
|
+
for k in kwargs.keys():
|
|
98
|
+
self._cfg[k.upper()] = kwargs[k]
|
|
99
|
+
|
|
100
|
+
def __getitem__(self, name):
|
|
101
|
+
"""Get a config item"""
|
|
102
|
+
|
|
103
|
+
# Load a variable from environment. It would have only been loaded in
|
|
104
|
+
# __init__ if it was also specified in the config file.
|
|
105
|
+
env_name = name.upper()
|
|
106
|
+
if name not in self._cfg and env_name in os.environ:
|
|
107
|
+
self._cfg[name] = os.environ[env_name]
|
|
108
|
+
|
|
109
|
+
return self._cfg[name]
|
|
110
|
+
|
|
111
|
+
def __setitem__(self, name, obj):
|
|
112
|
+
"""set config item"""
|
|
113
|
+
self._cfg[name] = obj
|
|
114
|
+
|
|
115
|
+
def __delitem__(self, name):
|
|
116
|
+
"""Delete option."""
|
|
117
|
+
del self._cfg[name]
|
|
118
|
+
|
|
119
|
+
def __len__(self):
|
|
120
|
+
return len(self._cfg)
|
|
121
|
+
|
|
122
|
+
def __iter__(self):
|
|
123
|
+
"""Iterate over config names"""
|
|
124
|
+
keys = self._cfg.keys()
|
|
125
|
+
for key in keys:
|
|
126
|
+
yield key
|
|
127
|
+
|
|
128
|
+
def __contains__(self, name):
|
|
129
|
+
return name in self._cfg or name.upper() in os.environ
|
|
130
|
+
|
|
131
|
+
def __repr__(self):
|
|
132
|
+
return json.dumps(self._cfg)
|
|
133
|
+
|
|
134
|
+
def keys(self):
|
|
135
|
+
return self._cfg.keys()
|
|
136
|
+
|
|
137
|
+
def get(self, name, default=None):
|
|
138
|
+
if name in self:
|
|
139
|
+
return self[name]
|
|
140
|
+
else:
|
|
141
|
+
return default
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def main():
|
|
145
|
+
parser = argparse.ArgumentParser(
|
|
146
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
147
|
+
usage=_HELP_USAGE,
|
|
148
|
+
epilog=_HELP_EPILOG,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
parser.add_argument(
|
|
152
|
+
"--root_dir",
|
|
153
|
+
type=str,
|
|
154
|
+
dest="root_dir",
|
|
155
|
+
help="Directory where to store the object store data",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--bucket_name",
|
|
159
|
+
type=str,
|
|
160
|
+
dest="bucket_name",
|
|
161
|
+
help='Name of the bucket to use (e.g., "hsds.test").',
|
|
162
|
+
)
|
|
163
|
+
parser.add_argument(
|
|
164
|
+
"--host", default="", type=str, dest="host", help="host name for url"
|
|
165
|
+
)
|
|
166
|
+
parser.add_argument(
|
|
167
|
+
"--hs_username",
|
|
168
|
+
type=str,
|
|
169
|
+
dest="hs_username",
|
|
170
|
+
help="username to be added to list of valid users",
|
|
171
|
+
default="",
|
|
172
|
+
)
|
|
173
|
+
parser.add_argument(
|
|
174
|
+
"--hs_password",
|
|
175
|
+
type=str,
|
|
176
|
+
dest="hs_password",
|
|
177
|
+
help="password for hs_username",
|
|
178
|
+
default="",
|
|
179
|
+
)
|
|
180
|
+
parser.add_argument(
|
|
181
|
+
"--password_file",
|
|
182
|
+
type=str,
|
|
183
|
+
dest="password_file",
|
|
184
|
+
help="location of hsds password file",
|
|
185
|
+
default="",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
parser.add_argument(
|
|
189
|
+
"--logfile",
|
|
190
|
+
default="",
|
|
191
|
+
type=str,
|
|
192
|
+
dest="logfile",
|
|
193
|
+
help="filename for logout (default stdout).",
|
|
194
|
+
)
|
|
195
|
+
parser.add_argument(
|
|
196
|
+
"--loglevel",
|
|
197
|
+
default="",
|
|
198
|
+
type=str,
|
|
199
|
+
dest="loglevel",
|
|
200
|
+
help="log verbosity: DEBUG, WARNING, INFO, OR ERROR",
|
|
201
|
+
)
|
|
202
|
+
parser.add_argument(
|
|
203
|
+
"-p", "--port", default=0, type=int, dest="port", help="Service node port"
|
|
204
|
+
)
|
|
205
|
+
parser.add_argument(
|
|
206
|
+
"--count",
|
|
207
|
+
default=4,
|
|
208
|
+
type=int,
|
|
209
|
+
dest="dn_count",
|
|
210
|
+
help="Number of dn sub-processes to create.",
|
|
211
|
+
)
|
|
212
|
+
parser.add_argument(
|
|
213
|
+
"--socket_dir",
|
|
214
|
+
default="",
|
|
215
|
+
type=str,
|
|
216
|
+
dest="socket_dir",
|
|
217
|
+
help="directory for socket endpoint",
|
|
218
|
+
)
|
|
219
|
+
parser.add_argument(
|
|
220
|
+
"--config_dir",
|
|
221
|
+
default="",
|
|
222
|
+
type=str,
|
|
223
|
+
dest="config_dir",
|
|
224
|
+
help="directory for config data",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
args = parser.parse_args()
|
|
228
|
+
|
|
229
|
+
kwargs = {} # options to pass to hsdsapp
|
|
230
|
+
|
|
231
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
232
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
233
|
+
|
|
234
|
+
# setup logging
|
|
235
|
+
if args.loglevel:
|
|
236
|
+
log_level_cfg = args.loglevel
|
|
237
|
+
kwargs["log_level"] = args.loglevel
|
|
238
|
+
elif "LOG_LEVEL" in os.environ:
|
|
239
|
+
log_level_cfg = os.environ["LOG_LEVEL"]
|
|
240
|
+
else:
|
|
241
|
+
log_level_cfg = "INFO"
|
|
242
|
+
if log_level_cfg == "DEBUG":
|
|
243
|
+
log_level = logging.DEBUG
|
|
244
|
+
elif log_level_cfg == "INFO":
|
|
245
|
+
log_level = logging.INFO
|
|
246
|
+
elif log_level_cfg in ("WARN", "WARNING"):
|
|
247
|
+
log_level = logging.WARN
|
|
248
|
+
elif log_level_cfg == "ERROR":
|
|
249
|
+
log_level = logging.ERROR
|
|
250
|
+
else:
|
|
251
|
+
print(f"unsupported log_level: {log_level_cfg}, using INFO instead")
|
|
252
|
+
log_level = logging.INFO
|
|
253
|
+
|
|
254
|
+
print("set logging to::", log_level)
|
|
255
|
+
logging.basicConfig(level=log_level)
|
|
256
|
+
|
|
257
|
+
userConfig = UserConfig()
|
|
258
|
+
|
|
259
|
+
login_username = None
|
|
260
|
+
try:
|
|
261
|
+
login_username = os.getlogin()
|
|
262
|
+
except OSError:
|
|
263
|
+
pass # ignore
|
|
264
|
+
|
|
265
|
+
username = None
|
|
266
|
+
password = None
|
|
267
|
+
|
|
268
|
+
# validate password_file arg if used
|
|
269
|
+
if args.password_file:
|
|
270
|
+
if not os.path.isfile(args.password_file):
|
|
271
|
+
sys.exit(f"password file: {args.password_file} not found")
|
|
272
|
+
kwargs["password_file"] = args.password_file
|
|
273
|
+
|
|
274
|
+
if args.hs_username:
|
|
275
|
+
sys.exit("username arg cannot be used if password_file is set")
|
|
276
|
+
if args.hs_password:
|
|
277
|
+
sys.exit("password arg cannot be used if password_file is set")
|
|
278
|
+
else:
|
|
279
|
+
if args.hs_username:
|
|
280
|
+
username = args.hs_username
|
|
281
|
+
elif "HS_USERNAME" in userConfig:
|
|
282
|
+
# can set username based on HS_USERNAME environment variable
|
|
283
|
+
username = userConfig["HS_USERNAME"]
|
|
284
|
+
else:
|
|
285
|
+
username = login_username
|
|
286
|
+
|
|
287
|
+
if args.hs_password:
|
|
288
|
+
password = args.hs_password
|
|
289
|
+
elif "HS_PASSWORD" in userConfig:
|
|
290
|
+
# can set password based on HS_PASSWORD environment variable
|
|
291
|
+
password = userConfig["HS_PASSWORD"]
|
|
292
|
+
else:
|
|
293
|
+
password = login_username
|
|
294
|
+
|
|
295
|
+
if username and password:
|
|
296
|
+
kwargs["username"] = username
|
|
297
|
+
kwargs["password"] = password
|
|
298
|
+
|
|
299
|
+
# use unix domain socket if a socket dir is set
|
|
300
|
+
if args.socket_dir:
|
|
301
|
+
socket_dir = os.path.abspath(args.socket_dir)
|
|
302
|
+
if not os.path.isdir(socket_dir):
|
|
303
|
+
raise FileNotFoundError(f"directory: {socket_dir} not found")
|
|
304
|
+
kwargs["socket_dir"] = socket_dir
|
|
305
|
+
else:
|
|
306
|
+
# USE TCP connect
|
|
307
|
+
if args.host:
|
|
308
|
+
kwargs["host"] = args.host
|
|
309
|
+
else:
|
|
310
|
+
kwargs["host"] = "localhost"
|
|
311
|
+
# sn_port only relevant for TCP connections
|
|
312
|
+
if args.port:
|
|
313
|
+
kwargs["sn_port"] = args.port
|
|
314
|
+
else:
|
|
315
|
+
kwargs["sn_port"] = 5101 # TBD - use config
|
|
316
|
+
|
|
317
|
+
if args.logfile:
|
|
318
|
+
logfile = os.path.abspath(args.logfile)
|
|
319
|
+
elif args.host:
|
|
320
|
+
logfile = os.path.abspath("hs.log")
|
|
321
|
+
else:
|
|
322
|
+
socket_dir = os.path.abspath(args.socket_dir)
|
|
323
|
+
logfile = os.path.join(socket_dir, "hs.log")
|
|
324
|
+
print("logfile:", logfile)
|
|
325
|
+
kwargs["logfile"] = logfile
|
|
326
|
+
|
|
327
|
+
if args.root_dir:
|
|
328
|
+
kwargs["root_dir"] = args.root_dir
|
|
329
|
+
|
|
330
|
+
config_dir = None
|
|
331
|
+
if args.config_dir:
|
|
332
|
+
if not os.path.isdir(args.config_dir):
|
|
333
|
+
print(f"config_dir: {args.config_dir} not found")
|
|
334
|
+
else:
|
|
335
|
+
config_dir = args.config_dir
|
|
336
|
+
if config_dir:
|
|
337
|
+
kwargs["config_dir"] = config_dir
|
|
338
|
+
|
|
339
|
+
if args.dn_count:
|
|
340
|
+
kwargs["dn_count"] = args.dn_count
|
|
341
|
+
|
|
342
|
+
if args.bucket_name:
|
|
343
|
+
bucket_name = args.bucket_name
|
|
344
|
+
else:
|
|
345
|
+
bucket_name = config.get("bucket_name")
|
|
346
|
+
if not bucket_name:
|
|
347
|
+
sys.exit("bucket_name not set")
|
|
348
|
+
if args.root_dir:
|
|
349
|
+
root_dir = args.root_dir
|
|
350
|
+
else:
|
|
351
|
+
root_dir = config.get("root_dir")
|
|
352
|
+
if not root_dir:
|
|
353
|
+
# check that AWS_S3_GATEWAY or AZURE_CONNECTION_STRING is set
|
|
354
|
+
if not config.get("aws_s3_gateway") and not config.get("azure_connection_string"):
|
|
355
|
+
sys.exit("root_dir not set (and no S3 or Azure connection info)")
|
|
356
|
+
else:
|
|
357
|
+
if not os.path.isdir(root_dir):
|
|
358
|
+
sys.exit(f"directory: {root_dir} not found")
|
|
359
|
+
bucket_path = os.path.join(root_dir, bucket_name)
|
|
360
|
+
if not os.path.isdir(bucket_path):
|
|
361
|
+
os.mkdir(bucket_path)
|
|
362
|
+
|
|
363
|
+
app = HsdsApp(**kwargs)
|
|
364
|
+
app.run()
|
|
365
|
+
|
|
366
|
+
waiting_on_ready = True
|
|
367
|
+
|
|
368
|
+
while True:
|
|
369
|
+
try:
|
|
370
|
+
time.sleep(1)
|
|
371
|
+
app.check_processes()
|
|
372
|
+
except KeyboardInterrupt:
|
|
373
|
+
print("got keyboard interrupt")
|
|
374
|
+
break
|
|
375
|
+
except Exception as e:
|
|
376
|
+
print(f"got exception: {e}")
|
|
377
|
+
break
|
|
378
|
+
if waiting_on_ready and app.ready:
|
|
379
|
+
waiting_on_ready = False
|
|
380
|
+
print("")
|
|
381
|
+
print("READY! use endpoint:", app.endpoint)
|
|
382
|
+
print("")
|
|
383
|
+
|
|
384
|
+
print("shutting down server")
|
|
385
|
+
app.stop()
|