rda-python-miscs 3.0.5__tar.gz → 3.0.6__tar.gz
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.
- {rda_python_miscs-3.0.5/src/rda_python_miscs.egg-info → rda_python_miscs-3.0.6}/PKG-INFO +30 -2
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/README.md +29 -1
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/pyproject.toml +2 -1
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexcp.py +1 -1
- rda_python_miscs-3.0.6/src/rda_python_miscs/gdexdrop.py +285 -0
- rda_python_miscs-3.0.6/src/rda_python_miscs/gdexdrop.usg +77 -0
- rda_python_miscs-3.0.6/src/rda_python_miscs/gdexdrop_standalone.py +23 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexkill.py +1 -1
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexmod.py +1 -1
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6/src/rda_python_miscs.egg-info}/PKG-INFO +30 -2
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/SOURCES.txt +3 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/entry_points.txt +1 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/tests/test_miscs.py +1 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/LICENSE +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/MANIFEST.in +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/setup.cfg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/__init__.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/bash_qsub.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/bashqsub.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/bashqsub.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/decsdata_restore.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/decsdata_restore.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/decsdata_storage.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/decsdata_storage.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdex_ls.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexcp.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexkill.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexls.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexls.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexls_standalone.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexmod.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexown.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexown.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexps.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexps.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexsub.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexsub.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexzip.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/gdexzip.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/pg_docs.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/pg_rst.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/pg_rst.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/pg_wget.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/pgwget.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/pgwget.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_cp.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_kill.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_mod.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_own.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_ps.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_sub.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rda_zip.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rdals.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rdals.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rst_templates/appendix.rst.temp +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rst_templates/index.rst.temp +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rst_templates/section.rst.temp +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/tcsh_qsub.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/tcshqsub.py +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/tcshqsub.usg +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/dependency_links.txt +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/requires.txt +0 -0
- {rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_miscs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.6
|
|
4
4
|
Summary: RDA Python package to hold RDA miscellaneous utility programs
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-miscs
|
|
@@ -43,6 +43,7 @@ The package provides two categories of programs:
|
|
|
43
43
|
| Command | Alias | Connector script | Description |
|
|
44
44
|
|---------|-------|-----------------|-------------|
|
|
45
45
|
| `gdexcp` | `rdacp` | `setuid_gdexcp` / `setuid_rdacp` | Copy files and directories across local, remote, Object Store, or Globus endpoints |
|
|
46
|
+
| `gdexdrop` | | `setuid_gdexdrop` | Copy local files into a GDEX dataset directory as gdexdata, for data providers outside the DECS group |
|
|
46
47
|
| `gdexkill` | `rdakill` | `setuid_gdexkill` / `setuid_rdakill` | Kill local processes and their children, or cancel PBS batch jobs |
|
|
47
48
|
| `gdexmod` | `rdamod` | `setuid_gdexmod` / `setuid_rdamod` | Change permission modes for files and directories owned by gdexdata |
|
|
48
49
|
| `decsdata_storage` | | `setuid_decsdata_storage` | Move decsdata datasets into the GLADE HSM cold storage to migrate them onto tape |
|
|
@@ -105,7 +106,7 @@ pip install rda_python_miscs
|
|
|
105
106
|
|
|
106
107
|
## Setuid Setup
|
|
107
108
|
|
|
108
|
-
The setuid programs (`gdexcp`, `gdexkill`, `gdexmod`, `decsdata_storage`,
|
|
109
|
+
The setuid programs (`gdexcp`, `gdexdrop`, `gdexkill`, `gdexmod`, `decsdata_storage`,
|
|
109
110
|
`decsdata_restore` and the `rda*` aliases) execute as the common user `PGLOG['COMMONUSER']` (default `gdexdata`) via
|
|
110
111
|
the `rda_python_setuid` mechanism, which is pulled in automatically as a
|
|
111
112
|
dependency. After `pip install` above, choose one of the wiring options
|
|
@@ -162,6 +163,33 @@ The shared setuid setup guide is shown automatically if any `setuid_*`
|
|
|
162
163
|
connector script is invoked directly before the setuid wrapper has been
|
|
163
164
|
configured.
|
|
164
165
|
|
|
166
|
+
## gdexdrop access list
|
|
167
|
+
|
|
168
|
+
`gdexdrop` lets data providers who are **not** in the DECS group copy files into
|
|
169
|
+
a dataset directory so that the result is owned by `gdexdata`. It is a
|
|
170
|
+
deliberately narrow alternative to `gdexcp`: the destination is always
|
|
171
|
+
`/glade/campaign/collections/gdex/data/<dsid>`, a path that escapes the dataset
|
|
172
|
+
directory is rejected, and every source must be readable by the calling user
|
|
173
|
+
rather than only by `gdexdata`.
|
|
174
|
+
|
|
175
|
+
Who may drop into which dataset is read from the access list
|
|
176
|
+
`/glade/u/home/gdexdata/config/gdexdrop.conf`. It must be owned by `gdexdata`
|
|
177
|
+
and must not be writable by group or others, otherwise `gdexdrop` refuses to
|
|
178
|
+
run. Each line is a login name, a colon, and the colon-separated dataset IDs
|
|
179
|
+
that login may drop into, or `all` for every dataset; `#` starts a comment:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
# login: dsid1[:...:dsidn]
|
|
183
|
+
jdoe: d123456:d654321
|
|
184
|
+
asmith: d111222
|
|
185
|
+
gdexhelp: all
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The root path, the access list path and the log path are compiled into the
|
|
189
|
+
program rather than taken from `PGLOG`, because `PGLOG['DSDHOME']`,
|
|
190
|
+
`PGLOG['DSSHOME']` and `PGLOG['LOGPATH']` are all settable from environment
|
|
191
|
+
variables of the same name, which the caller controls.
|
|
192
|
+
|
|
165
193
|
## Cold storage setup
|
|
166
194
|
|
|
167
195
|
`decsdata_storage` and `decsdata_restore` drive the GLADE HSM through the
|
|
@@ -26,6 +26,7 @@ The package provides two categories of programs:
|
|
|
26
26
|
| Command | Alias | Connector script | Description |
|
|
27
27
|
|---------|-------|-----------------|-------------|
|
|
28
28
|
| `gdexcp` | `rdacp` | `setuid_gdexcp` / `setuid_rdacp` | Copy files and directories across local, remote, Object Store, or Globus endpoints |
|
|
29
|
+
| `gdexdrop` | | `setuid_gdexdrop` | Copy local files into a GDEX dataset directory as gdexdata, for data providers outside the DECS group |
|
|
29
30
|
| `gdexkill` | `rdakill` | `setuid_gdexkill` / `setuid_rdakill` | Kill local processes and their children, or cancel PBS batch jobs |
|
|
30
31
|
| `gdexmod` | `rdamod` | `setuid_gdexmod` / `setuid_rdamod` | Change permission modes for files and directories owned by gdexdata |
|
|
31
32
|
| `decsdata_storage` | | `setuid_decsdata_storage` | Move decsdata datasets into the GLADE HSM cold storage to migrate them onto tape |
|
|
@@ -88,7 +89,7 @@ pip install rda_python_miscs
|
|
|
88
89
|
|
|
89
90
|
## Setuid Setup
|
|
90
91
|
|
|
91
|
-
The setuid programs (`gdexcp`, `gdexkill`, `gdexmod`, `decsdata_storage`,
|
|
92
|
+
The setuid programs (`gdexcp`, `gdexdrop`, `gdexkill`, `gdexmod`, `decsdata_storage`,
|
|
92
93
|
`decsdata_restore` and the `rda*` aliases) execute as the common user `PGLOG['COMMONUSER']` (default `gdexdata`) via
|
|
93
94
|
the `rda_python_setuid` mechanism, which is pulled in automatically as a
|
|
94
95
|
dependency. After `pip install` above, choose one of the wiring options
|
|
@@ -145,6 +146,33 @@ The shared setuid setup guide is shown automatically if any `setuid_*`
|
|
|
145
146
|
connector script is invoked directly before the setuid wrapper has been
|
|
146
147
|
configured.
|
|
147
148
|
|
|
149
|
+
## gdexdrop access list
|
|
150
|
+
|
|
151
|
+
`gdexdrop` lets data providers who are **not** in the DECS group copy files into
|
|
152
|
+
a dataset directory so that the result is owned by `gdexdata`. It is a
|
|
153
|
+
deliberately narrow alternative to `gdexcp`: the destination is always
|
|
154
|
+
`/glade/campaign/collections/gdex/data/<dsid>`, a path that escapes the dataset
|
|
155
|
+
directory is rejected, and every source must be readable by the calling user
|
|
156
|
+
rather than only by `gdexdata`.
|
|
157
|
+
|
|
158
|
+
Who may drop into which dataset is read from the access list
|
|
159
|
+
`/glade/u/home/gdexdata/config/gdexdrop.conf`. It must be owned by `gdexdata`
|
|
160
|
+
and must not be writable by group or others, otherwise `gdexdrop` refuses to
|
|
161
|
+
run. Each line is a login name, a colon, and the colon-separated dataset IDs
|
|
162
|
+
that login may drop into, or `all` for every dataset; `#` starts a comment:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
# login: dsid1[:...:dsidn]
|
|
166
|
+
jdoe: d123456:d654321
|
|
167
|
+
asmith: d111222
|
|
168
|
+
gdexhelp: all
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The root path, the access list path and the log path are compiled into the
|
|
172
|
+
program rather than taken from `PGLOG`, because `PGLOG['DSDHOME']`,
|
|
173
|
+
`PGLOG['DSSHOME']` and `PGLOG['LOGPATH']` are all settable from environment
|
|
174
|
+
variables of the same name, which the caller controls.
|
|
175
|
+
|
|
148
176
|
## Cold storage setup
|
|
149
177
|
|
|
150
178
|
`decsdata_storage` and `decsdata_restore` drive the GLADE HSM through the
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "rda_python_miscs"
|
|
9
|
-
version = "3.0.
|
|
9
|
+
version = "3.0.6"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="Zaihua Ji", email="zji@ucar.edu" },
|
|
12
12
|
]
|
|
@@ -47,6 +47,7 @@ pythonpath = [
|
|
|
47
47
|
"rdaown" = "rda_python_miscs.gdexown:main"
|
|
48
48
|
"setuid_gdexcp" = "rda_python_miscs.gdexcp:main"
|
|
49
49
|
"setuid_rdacp" = "rda_python_miscs.gdexcp:main"
|
|
50
|
+
"setuid_gdexdrop" = "rda_python_miscs.gdexdrop:main"
|
|
50
51
|
"setuid_gdexkill" = "rda_python_miscs.gdexkill:main"
|
|
51
52
|
"setuid_rdakill" = "rda_python_miscs.gdexkill:main"
|
|
52
53
|
"setuid_gdexmod" = "rda_python_miscs.gdexmod:main"
|
|
@@ -378,7 +378,7 @@ def main():
|
|
|
378
378
|
"""Entry point: instantiate GdexCp, parse arguments, run, and exit."""
|
|
379
379
|
from rda_python_setuid.setup_guide import show_setup_guide
|
|
380
380
|
object = GdexCp()
|
|
381
|
-
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexkill', 'gdexmod'])
|
|
381
|
+
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexdrop', 'gdexkill', 'gdexmod'])
|
|
382
382
|
object.read_parameters()
|
|
383
383
|
object.start_actions()
|
|
384
384
|
object.pgexit(0)
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
##################################################################################
|
|
3
|
+
# Title: gdexdrop
|
|
4
|
+
# Author: Zaihua Ji, zji@ucar.edu
|
|
5
|
+
# Date: 2026-09-23
|
|
6
|
+
# Purpose: copy local files into a GDEX dataset directory as 'gdexdata', for
|
|
7
|
+
# data providers who are not in the DECS group. Unlike gdexcp, the
|
|
8
|
+
# destination is confined to <DROPROOT>/<dsid> and the caller must be
|
|
9
|
+
# listed in the gdexdrop access list.
|
|
10
|
+
# Github: https://github.com/NCAR/rda-python-miscs.git
|
|
11
|
+
##################################################################################
|
|
12
|
+
import re
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
from os import path as op
|
|
16
|
+
from rda_python_common.pg_file import PgFile
|
|
17
|
+
|
|
18
|
+
# These are hardcoded on purpose. gdexdrop runs setuid to 'gdexdata' for callers
|
|
19
|
+
# who are NOT in the DECS group, so nothing deciding WHERE it writes or WHO may
|
|
20
|
+
# write there can come from the caller. PgLOG.SETPGLOG() fills PGLOG['DSDHOME'],
|
|
21
|
+
# PGLOG['DSSHOME'] and PGLOG['LOGPATH'] from environment variables of the same
|
|
22
|
+
# names, which the caller controls, so those values are deliberately not used.
|
|
23
|
+
DROPROOT = "/glade/campaign/collections/gdex/data" # dataset data root path
|
|
24
|
+
DROPCONF = "/glade/u/home/gdexdata/config/gdexdrop.conf" # access list
|
|
25
|
+
DROPLOG = "/glade/u/home/gdexdata/dssdb/log" # log path
|
|
26
|
+
DROPUSER = "gdexdata" # owner of dropped files
|
|
27
|
+
|
|
28
|
+
class GdexDrop(PgFile):
|
|
29
|
+
"""Copy local files and directories into a GDEX dataset directory as 'gdexdata'.
|
|
30
|
+
|
|
31
|
+
The destination is always <DROPROOT>/<dsid>, optionally with a sub-path given
|
|
32
|
+
by -t, and is verified to stay inside that dataset directory. The caller must
|
|
33
|
+
be granted the dataset in the gdexdrop access list, and must be able to read
|
|
34
|
+
every source path as themselves; both checks exist because the copy itself
|
|
35
|
+
runs with the privileges of 'gdexdata'.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
"""Initialize GdexDrop with default drop options and runtime state."""
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.DROP = {
|
|
42
|
+
'ds': None, # target dataset ID, such as d123456
|
|
43
|
+
'f': [], # from file/directory names
|
|
44
|
+
'i': None, # input file holding a list of from file names, one per line
|
|
45
|
+
't': None, # sub-path under the dataset directory to drop the files into
|
|
46
|
+
'O': 0, # 1 to override an existing target file of the same size
|
|
47
|
+
'F': 0o664, # to file mode, default to 664
|
|
48
|
+
'D': 0o775, # to directory mode, default to 775
|
|
49
|
+
}
|
|
50
|
+
self.DINFO = {
|
|
51
|
+
'tcnt': 0, # count of files/directories copied
|
|
52
|
+
'target': None, # resolved target directory
|
|
53
|
+
'curdir': os.getcwd()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# function to read parameters
|
|
57
|
+
def read_parameters(self):
|
|
58
|
+
"""Parse command-line arguments, verify read access, then switch to 'gdexdata'.
|
|
59
|
+
|
|
60
|
+
The default option is -f (source paths); positional arguments are treated
|
|
61
|
+
as source paths wherever they appear. -O is a boolean flag; -F and -D take
|
|
62
|
+
octal modes. The source access check must run before set_suid(), while the
|
|
63
|
+
real user ID is still the caller, so that os.access() reports what the
|
|
64
|
+
CALLER can read rather than what 'gdexdata' can read.
|
|
65
|
+
"""
|
|
66
|
+
dohelp = 0
|
|
67
|
+
argv = sys.argv[1:]
|
|
68
|
+
self.set_help_path(__file__)
|
|
69
|
+
self.PGLOG['LOGPATH'] = DROPLOG
|
|
70
|
+
self.PGLOG['LOGFILE'] = "gdexdrop.log" # set different log file
|
|
71
|
+
defopt = option = 'f'
|
|
72
|
+
for arg in argv:
|
|
73
|
+
if re.match(r'-(h|-help)$', arg, re.I):
|
|
74
|
+
dohelp = 1
|
|
75
|
+
continue
|
|
76
|
+
ms = re.match(r'-(\w+)$', arg)
|
|
77
|
+
if ms:
|
|
78
|
+
option = ms.group(1)
|
|
79
|
+
if option not in self.DROP: self.pglog(arg + ": Unknown Option", self.LGEREX)
|
|
80
|
+
if option == 'O':
|
|
81
|
+
self.DROP[option] = 1
|
|
82
|
+
option = defopt
|
|
83
|
+
continue
|
|
84
|
+
if not option: self.pglog(arg + ": Value provided without option", self.LGEREX)
|
|
85
|
+
if option == 'f':
|
|
86
|
+
self.DROP['f'].append(arg)
|
|
87
|
+
else:
|
|
88
|
+
if option in ('F', 'D'):
|
|
89
|
+
self.DROP[option] = self.base2int(arg, 8)
|
|
90
|
+
else:
|
|
91
|
+
self.DROP[option] = arg
|
|
92
|
+
option = defopt
|
|
93
|
+
if self.DROP['i']: self.add_input_files(self.DROP['i'])
|
|
94
|
+
if dohelp or not self.DROP['ds'] or not self.DROP['f']: self.show_usage("gdexdrop")
|
|
95
|
+
self.check_source_access()
|
|
96
|
+
self.set_suid(self.PGLOG['EUID'])
|
|
97
|
+
self.cmdlog("gdexdrop {} ({})".format(' '.join(argv), self.DINFO['curdir']))
|
|
98
|
+
|
|
99
|
+
# read source paths from an input file and append them to the -f list
|
|
100
|
+
def add_input_files(self, infile):
|
|
101
|
+
"""Append source paths read from an input file to the -f source list.
|
|
102
|
+
|
|
103
|
+
Each non-empty line is treated as one source path; leading/trailing
|
|
104
|
+
whitespace is stripped and lines starting with '#' are ignored. The file
|
|
105
|
+
must be readable by the caller, not merely by 'gdexdata'.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
infile (str): Path to the input file holding one source path per line.
|
|
109
|
+
"""
|
|
110
|
+
if not os.access(infile, os.R_OK):
|
|
111
|
+
self.pglog("{}: Input file of -i not readable by {}".format(infile, self.PGLOG['CURUID']), self.LGEREX)
|
|
112
|
+
fd = open(infile, 'r')
|
|
113
|
+
for line in fd:
|
|
114
|
+
line = line.strip()
|
|
115
|
+
if not line or line[0] == '#': continue
|
|
116
|
+
self.DROP['f'].append(line)
|
|
117
|
+
fd.close()
|
|
118
|
+
|
|
119
|
+
# make sure the caller can read every source path as themselves
|
|
120
|
+
def check_source_access(self):
|
|
121
|
+
"""Verify the calling user can read each source path, exiting if not.
|
|
122
|
+
|
|
123
|
+
Must be called while the real user ID is still the caller, i.e. before
|
|
124
|
+
set_suid(). Without this check a caller could name a path readable only by
|
|
125
|
+
'gdexdata', have it copied into a dataset directory they can read, and so
|
|
126
|
+
read it indirectly.
|
|
127
|
+
"""
|
|
128
|
+
for file in self.DROP['f']:
|
|
129
|
+
path = re.sub(r'/$', '', file, 1)
|
|
130
|
+
if not re.match(r'^/', path): path = self.join_paths(self.DINFO['curdir'], path)
|
|
131
|
+
if not op.exists(path):
|
|
132
|
+
self.pglog("{}: {}".format(file, self.PGLOG['MISSFILE']), self.LGEREX)
|
|
133
|
+
if not self.caller_can_read(path):
|
|
134
|
+
self.pglog("{}: not readable by {}".format(file, self.PGLOG['CURUID']), self.LGEREX)
|
|
135
|
+
|
|
136
|
+
# check read access of one path, and of everything under it for a directory
|
|
137
|
+
def caller_can_read(self, path):
|
|
138
|
+
"""Return 1 if the real user can read path and all of its contents.
|
|
139
|
+
|
|
140
|
+
os.access() tests the REAL user and group IDs, which is exactly what is
|
|
141
|
+
wanted here while running with an effective ID of 'gdexdata'.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
path (str): Absolute source path to check.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
int: 1 if fully readable by the calling user, 0 otherwise.
|
|
148
|
+
"""
|
|
149
|
+
if not os.access(path, os.R_OK): return 0
|
|
150
|
+
if op.isfile(path): return 1
|
|
151
|
+
if not os.access(path, os.X_OK): return 0
|
|
152
|
+
for root, dirs, files in os.walk(path):
|
|
153
|
+
for name in dirs:
|
|
154
|
+
if not os.access(op.join(root, name), os.R_OK|os.X_OK): return 0
|
|
155
|
+
for name in files:
|
|
156
|
+
if not os.access(op.join(root, name), os.R_OK): return 0
|
|
157
|
+
return 1
|
|
158
|
+
|
|
159
|
+
# function to start actions
|
|
160
|
+
def start_actions(self):
|
|
161
|
+
"""Resolve and authorize the target dataset directory, then copy the sources.
|
|
162
|
+
|
|
163
|
+
Logs a summary count on completion.
|
|
164
|
+
"""
|
|
165
|
+
self.DINFO['target'] = self.resolve_target()
|
|
166
|
+
self.validate_caller()
|
|
167
|
+
self.PGLOG['FILEMODE'] = self.DROP['F']
|
|
168
|
+
self.PGLOG['EXECMODE'] = self.DROP['D']
|
|
169
|
+
for file in self.DROP['f']:
|
|
170
|
+
self.DINFO['tcnt'] += self.drop_one(file)
|
|
171
|
+
if self.DINFO['tcnt'] > 0:
|
|
172
|
+
s = ('s' if self.DINFO['tcnt'] > 1 else '')
|
|
173
|
+
self.pglog("Total {} File{} dropped into {}".format(self.DINFO['tcnt'], s, self.DINFO['target']), self.LOGWRN)
|
|
174
|
+
else:
|
|
175
|
+
self.pglog("No File dropped into " + self.DINFO['target'], self.LOGWRN)
|
|
176
|
+
self.cmdlog()
|
|
177
|
+
|
|
178
|
+
# resolve the target directory and confine it to the dataset directory
|
|
179
|
+
def resolve_target(self):
|
|
180
|
+
"""Return the target directory under DROPROOT for -ds, confined to the dataset.
|
|
181
|
+
|
|
182
|
+
The resolved real path of the target must equal, or lie under, the resolved
|
|
183
|
+
real path of the dataset directory, so that neither a '..' component in -t
|
|
184
|
+
nor an existing symbolic link can move the drop outside the dataset.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
str: Resolved absolute target directory.
|
|
188
|
+
"""
|
|
189
|
+
dsid = self.DROP['ds']
|
|
190
|
+
if not re.match(r'^[a-z]\d{6}$', dsid):
|
|
191
|
+
self.pglog(dsid + ": Invalid Dataset ID of -ds, expecting the form d123456", self.LGEREX)
|
|
192
|
+
dsroot = op.realpath(op.join(DROPROOT, dsid))
|
|
193
|
+
if not op.isdir(dsroot):
|
|
194
|
+
self.pglog("{}: Dataset directory NOT exists under {}".format(dsid, DROPROOT), self.LGEREX)
|
|
195
|
+
target = self.confine_path(op.join(dsroot, self.DROP['t']) if self.DROP['t'] else dsroot, dsroot)
|
|
196
|
+
return target
|
|
197
|
+
|
|
198
|
+
# make sure a path stays inside the dataset directory
|
|
199
|
+
def confine_path(self, path, dsroot):
|
|
200
|
+
"""Return the resolved path, exiting if it falls outside the dataset directory.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
path (str): Candidate absolute path.
|
|
204
|
+
dsroot (str): Resolved absolute dataset directory.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
str: Resolved absolute path inside dsroot.
|
|
208
|
+
"""
|
|
209
|
+
rpath = op.realpath(path)
|
|
210
|
+
if rpath != dsroot and not rpath.startswith(dsroot + '/'):
|
|
211
|
+
self.pglog("{}: Target path is outside of {}".format(path, dsroot), self.LGEREX)
|
|
212
|
+
return rpath
|
|
213
|
+
|
|
214
|
+
# check the caller is granted the target dataset in the access list
|
|
215
|
+
def validate_caller(self):
|
|
216
|
+
"""Exit unless the calling user is granted the target dataset.
|
|
217
|
+
|
|
218
|
+
The access list must be owned by 'gdexdata' and must not be writable by
|
|
219
|
+
group or others; otherwise anyone able to edit it could grant themselves
|
|
220
|
+
access to any dataset. Each line is 'login: dsid1[:...:dsidn]', or
|
|
221
|
+
'login: all' for every dataset; '#' starts a comment. The check is
|
|
222
|
+
skipped when 'gdexdata' itself runs the command.
|
|
223
|
+
"""
|
|
224
|
+
logname = self.PGLOG['CURUID']
|
|
225
|
+
if logname == DROPUSER: return
|
|
226
|
+
info = self.check_local_file(DROPCONF, 6, self.LOGWRN)
|
|
227
|
+
if not info:
|
|
228
|
+
self.pglog(DROPCONF + ": gdexdrop access list NOT exists", self.LGEREX)
|
|
229
|
+
if info['logname'] != DROPUSER:
|
|
230
|
+
self.pglog("{}: access list must be owned by {}, but is owned by {}".format(DROPCONF, DROPUSER, info['logname']), self.LGEREX)
|
|
231
|
+
if info['mode']&0o022:
|
|
232
|
+
self.pglog("{}: access list must not be group or world writable, mode is {:o}".format(DROPCONF, info['mode']), self.LGEREX)
|
|
233
|
+
dsids = {}
|
|
234
|
+
fd = open(DROPCONF, 'r')
|
|
235
|
+
for line in fd:
|
|
236
|
+
line = re.sub(r'#.*$', '', line).strip()
|
|
237
|
+
if not line: continue
|
|
238
|
+
ms = re.match(r'^(\S+)\s*:\s*(.*)$', line)
|
|
239
|
+
if not ms: continue
|
|
240
|
+
if ms.group(1) != logname: continue
|
|
241
|
+
for dsid in ms.group(2).split(':'):
|
|
242
|
+
dsid = dsid.strip()
|
|
243
|
+
if dsid: dsids[dsid] = 1
|
|
244
|
+
fd.close()
|
|
245
|
+
if not ('all' in dsids or self.DROP['ds'] in dsids):
|
|
246
|
+
self.pglog("{}: NOT granted Dataset {} in {}".format(logname, self.DROP['ds'], DROPCONF), self.LGEREX)
|
|
247
|
+
|
|
248
|
+
# copy one source path into the target directory
|
|
249
|
+
def drop_one(self, file):
|
|
250
|
+
"""Copy one source file or directory into the target directory.
|
|
251
|
+
|
|
252
|
+
A directory is copied recursively, as the directory itself. An existing
|
|
253
|
+
target file of the same size is left alone unless -O is given.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
file (str): Source path as given on the command line.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
int: 1 if the source was copied, 0 otherwise.
|
|
260
|
+
"""
|
|
261
|
+
path = re.sub(r'/$', '', file, 1)
|
|
262
|
+
if not re.match(r'^/', path): path = self.join_paths(self.DINFO['curdir'], path)
|
|
263
|
+
finfo = self.check_local_file(path, 0, self.LOGWRN)
|
|
264
|
+
if not finfo:
|
|
265
|
+
return self.pglog("{}: {}".format(file, self.PGLOG['MISSFILE']), self.LOGERR)
|
|
266
|
+
tofile = self.confine_path(op.join(self.DINFO['target'], op.basename(path)), self.DINFO['target'])
|
|
267
|
+
if finfo['isfile'] and not self.DROP['O']:
|
|
268
|
+
tinfo = self.check_local_file(tofile, 0, self.LOGWRN)
|
|
269
|
+
if tinfo and tinfo['data_size'] == finfo['data_size']:
|
|
270
|
+
self.pglog(tofile + ": Target exists with same size, skip copying", self.LOGWRN)
|
|
271
|
+
return 0
|
|
272
|
+
return (1 if self.local_copy_local(tofile, path, self.LGWNEX) else 0)
|
|
273
|
+
|
|
274
|
+
# main function to execute this script
|
|
275
|
+
def main():
|
|
276
|
+
"""Entry point: instantiate GdexDrop, parse arguments, run, and exit."""
|
|
277
|
+
from rda_python_setuid.setup_guide import show_setup_guide
|
|
278
|
+
object = GdexDrop()
|
|
279
|
+
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexdrop', 'gdexkill', 'gdexmod'])
|
|
280
|
+
object.read_parameters()
|
|
281
|
+
object.start_actions()
|
|
282
|
+
object.pgexit(0)
|
|
283
|
+
|
|
284
|
+
# call main() to start program
|
|
285
|
+
if __name__ == "__main__": main()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
Copy local files and directories into a GDEX dataset directory so that they end
|
|
3
|
+
up owned by 'gdexdata'. It is a restricted alternative to gdexcp, intended for
|
|
4
|
+
data providers who are not in the DECS group: the destination is always the
|
|
5
|
+
given dataset's directory, and the calling user must be granted that dataset in
|
|
6
|
+
the gdexdrop access list.
|
|
7
|
+
|
|
8
|
+
Usage: gdexdrop -ds DatasetID [-t TargetPath] [-i InputFile] [-O] [-h] \
|
|
9
|
+
[-D DirectoryMode] [-F FileMode] File/Directory List
|
|
10
|
+
|
|
11
|
+
- Option -ds DatasetID is mandatory. It names the dataset to drop the
|
|
12
|
+
files into, in the form d123456. The files are copied under
|
|
13
|
+
/glade/campaign/collections/gdex/data/DatasetID, which must already
|
|
14
|
+
exist;
|
|
15
|
+
|
|
16
|
+
- Option -t TargetPath, sub-path under the dataset directory to drop the
|
|
17
|
+
files into. Missing sub-directories are created. The path must stay
|
|
18
|
+
inside the dataset directory; '..' components and symbolic links
|
|
19
|
+
leading out of it are rejected. Defaults to the dataset directory
|
|
20
|
+
itself;
|
|
21
|
+
|
|
22
|
+
- Option -f File/Directory List is the default option, so the source paths
|
|
23
|
+
may be given without it and in any position. Relative paths are
|
|
24
|
+
resolved against the current directory. A directory is copied
|
|
25
|
+
recursively, as the directory itself;
|
|
26
|
+
|
|
27
|
+
- Option -i InputFile, read additional source paths from a file, one per
|
|
28
|
+
line. Blank lines and lines starting with '#' are ignored;
|
|
29
|
+
|
|
30
|
+
- Option -O, override an existing target file of the same size. Without
|
|
31
|
+
it such a file is left alone and the copy is skipped;
|
|
32
|
+
|
|
33
|
+
- Option -D DirectoryMode, permission mode for created directories in
|
|
34
|
+
octal notation. Defaults to 775;
|
|
35
|
+
|
|
36
|
+
- Option -F FileMode, permission mode for copied files in octal notation.
|
|
37
|
+
Defaults to 664;
|
|
38
|
+
|
|
39
|
+
- Option -h, display this help document.
|
|
40
|
+
|
|
41
|
+
Every source path must be readable by the calling user, not merely by
|
|
42
|
+
'gdexdata'; a path the caller cannot read themselves is refused. Copied files
|
|
43
|
+
and directories are owned by 'gdexdata'.
|
|
44
|
+
|
|
45
|
+
Access List
|
|
46
|
+
-----------
|
|
47
|
+
|
|
48
|
+
The access list is /glade/u/home/gdexdata/config/gdexdrop.conf. It must be
|
|
49
|
+
owned by 'gdexdata' and must not be writable by group or others. Each line
|
|
50
|
+
is a login name, a colon, and the colon-separated dataset IDs that login may
|
|
51
|
+
drop into, or 'all' for every dataset. '#' starts a comment:
|
|
52
|
+
|
|
53
|
+
# login: dsid1[:...:dsidn]
|
|
54
|
+
jdoe: d123456:d654321
|
|
55
|
+
asmith: d111222
|
|
56
|
+
gdexhelp: all
|
|
57
|
+
|
|
58
|
+
A login absent from the list cannot drop into any dataset. 'gdexdata' itself
|
|
59
|
+
is always allowed and needs no entry.
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
|
|
63
|
+
1. Drop two files into the top of a dataset directory:
|
|
64
|
+
|
|
65
|
+
gdexdrop -ds d123456 hourly.202601.nc hourly.202602.nc
|
|
66
|
+
|
|
67
|
+
2. Drop the same files into a sub-directory of the dataset:
|
|
68
|
+
|
|
69
|
+
gdexdrop -ds d123456 -t 2026/monthly hourly.2026*.nc
|
|
70
|
+
|
|
71
|
+
3. Drop a whole directory tree, replacing files already there:
|
|
72
|
+
|
|
73
|
+
gdexdrop -ds d123456 -t archive -O /path/to/mydir
|
|
74
|
+
|
|
75
|
+
4. Drop a long list of files named in a file, with group-writable modes:
|
|
76
|
+
|
|
77
|
+
gdexdrop -ds d123456 -i filelist.txt -F 664 -D 775
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/glade/work/zji/conda-envs/pg-gdex/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# 2026-09-23, zji@ucar.edu, created for a standalone version of gdexdrop
|
|
4
|
+
#
|
|
5
|
+
# Copy this file to a common area, such as /glade/u/home/gdexdata/bin, so that
|
|
6
|
+
# users without the conda environment or a venv activated can run gdexdrop.
|
|
7
|
+
# The shebang and pgpath below name the environment gdexdrop is installed in.
|
|
8
|
+
#
|
|
9
|
+
# Run directly, it executes as the CALLING user, so the dropped files are owned
|
|
10
|
+
# by the caller. To have them owned by 'gdexdata' for a caller outside the DECS
|
|
11
|
+
# group, compile a 4755 cmwrapper binary that execs this script:
|
|
12
|
+
#
|
|
13
|
+
# pywrapper-install -m gdexdrop -t <common area>/gdexdrop_standalone.py \
|
|
14
|
+
# -d <common area>
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
pgpath = '/glade/work/zji/conda-envs/pg-gdex/lib/python3.12/site-packages'
|
|
18
|
+
if pgpath not in sys.path: sys.path.insert(0, pgpath)
|
|
19
|
+
|
|
20
|
+
from rda_python_miscs.gdexdrop import main
|
|
21
|
+
if __name__ == '__main__':
|
|
22
|
+
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
|
23
|
+
sys.exit(main())
|
|
@@ -260,7 +260,7 @@ def main():
|
|
|
260
260
|
"""Entry point: instantiate GdexKill, parse arguments, run, and exit."""
|
|
261
261
|
from rda_python_setuid.setup_guide import show_setup_guide
|
|
262
262
|
object = GdexKill()
|
|
263
|
-
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexkill', 'gdexmod'])
|
|
263
|
+
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexdrop', 'gdexkill', 'gdexmod'])
|
|
264
264
|
object.read_parameters()
|
|
265
265
|
object.start_actions()
|
|
266
266
|
object.pgexit(0)
|
|
@@ -196,7 +196,7 @@ def main():
|
|
|
196
196
|
"""Entry point: instantiate GdexMod, parse arguments, run, and exit."""
|
|
197
197
|
from rda_python_setuid.setup_guide import show_setup_guide
|
|
198
198
|
object = GdexMod()
|
|
199
|
-
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexkill', 'gdexmod'])
|
|
199
|
+
show_setup_guide(object, 'rda_python_miscs', ['gdexcp', 'gdexdrop', 'gdexkill', 'gdexmod'])
|
|
200
200
|
object.read_parameters()
|
|
201
201
|
object.start_actions()
|
|
202
202
|
object.pgexit(0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_miscs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.6
|
|
4
4
|
Summary: RDA Python package to hold RDA miscellaneous utility programs
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-miscs
|
|
@@ -43,6 +43,7 @@ The package provides two categories of programs:
|
|
|
43
43
|
| Command | Alias | Connector script | Description |
|
|
44
44
|
|---------|-------|-----------------|-------------|
|
|
45
45
|
| `gdexcp` | `rdacp` | `setuid_gdexcp` / `setuid_rdacp` | Copy files and directories across local, remote, Object Store, or Globus endpoints |
|
|
46
|
+
| `gdexdrop` | | `setuid_gdexdrop` | Copy local files into a GDEX dataset directory as gdexdata, for data providers outside the DECS group |
|
|
46
47
|
| `gdexkill` | `rdakill` | `setuid_gdexkill` / `setuid_rdakill` | Kill local processes and their children, or cancel PBS batch jobs |
|
|
47
48
|
| `gdexmod` | `rdamod` | `setuid_gdexmod` / `setuid_rdamod` | Change permission modes for files and directories owned by gdexdata |
|
|
48
49
|
| `decsdata_storage` | | `setuid_decsdata_storage` | Move decsdata datasets into the GLADE HSM cold storage to migrate them onto tape |
|
|
@@ -105,7 +106,7 @@ pip install rda_python_miscs
|
|
|
105
106
|
|
|
106
107
|
## Setuid Setup
|
|
107
108
|
|
|
108
|
-
The setuid programs (`gdexcp`, `gdexkill`, `gdexmod`, `decsdata_storage`,
|
|
109
|
+
The setuid programs (`gdexcp`, `gdexdrop`, `gdexkill`, `gdexmod`, `decsdata_storage`,
|
|
109
110
|
`decsdata_restore` and the `rda*` aliases) execute as the common user `PGLOG['COMMONUSER']` (default `gdexdata`) via
|
|
110
111
|
the `rda_python_setuid` mechanism, which is pulled in automatically as a
|
|
111
112
|
dependency. After `pip install` above, choose one of the wiring options
|
|
@@ -162,6 +163,33 @@ The shared setuid setup guide is shown automatically if any `setuid_*`
|
|
|
162
163
|
connector script is invoked directly before the setuid wrapper has been
|
|
163
164
|
configured.
|
|
164
165
|
|
|
166
|
+
## gdexdrop access list
|
|
167
|
+
|
|
168
|
+
`gdexdrop` lets data providers who are **not** in the DECS group copy files into
|
|
169
|
+
a dataset directory so that the result is owned by `gdexdata`. It is a
|
|
170
|
+
deliberately narrow alternative to `gdexcp`: the destination is always
|
|
171
|
+
`/glade/campaign/collections/gdex/data/<dsid>`, a path that escapes the dataset
|
|
172
|
+
directory is rejected, and every source must be readable by the calling user
|
|
173
|
+
rather than only by `gdexdata`.
|
|
174
|
+
|
|
175
|
+
Who may drop into which dataset is read from the access list
|
|
176
|
+
`/glade/u/home/gdexdata/config/gdexdrop.conf`. It must be owned by `gdexdata`
|
|
177
|
+
and must not be writable by group or others, otherwise `gdexdrop` refuses to
|
|
178
|
+
run. Each line is a login name, a colon, and the colon-separated dataset IDs
|
|
179
|
+
that login may drop into, or `all` for every dataset; `#` starts a comment:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
# login: dsid1[:...:dsidn]
|
|
183
|
+
jdoe: d123456:d654321
|
|
184
|
+
asmith: d111222
|
|
185
|
+
gdexhelp: all
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The root path, the access list path and the log path are compiled into the
|
|
189
|
+
program rather than taken from `PGLOG`, because `PGLOG['DSDHOME']`,
|
|
190
|
+
`PGLOG['DSSHOME']` and `PGLOG['LOGPATH']` are all settable from environment
|
|
191
|
+
variables of the same name, which the caller controls.
|
|
192
|
+
|
|
165
193
|
## Cold storage setup
|
|
166
194
|
|
|
167
195
|
`decsdata_storage` and `decsdata_restore` drive the GLADE HSM through the
|
|
@@ -13,6 +13,9 @@ src/rda_python_miscs/decsdata_storage.usg
|
|
|
13
13
|
src/rda_python_miscs/gdex_ls.py
|
|
14
14
|
src/rda_python_miscs/gdexcp.py
|
|
15
15
|
src/rda_python_miscs/gdexcp.usg
|
|
16
|
+
src/rda_python_miscs/gdexdrop.py
|
|
17
|
+
src/rda_python_miscs/gdexdrop.usg
|
|
18
|
+
src/rda_python_miscs/gdexdrop_standalone.py
|
|
16
19
|
src/rda_python_miscs/gdexkill.py
|
|
17
20
|
src/rda_python_miscs/gdexkill.usg
|
|
18
21
|
src/rda_python_miscs/gdexls.py
|
{rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/entry_points.txt
RENAMED
|
@@ -14,6 +14,7 @@ rdazip = rda_python_miscs.gdexzip:main
|
|
|
14
14
|
setuid_decsdata_restore = rda_python_miscs.decsdata_restore:main
|
|
15
15
|
setuid_decsdata_storage = rda_python_miscs.decsdata_storage:main
|
|
16
16
|
setuid_gdexcp = rda_python_miscs.gdexcp:main
|
|
17
|
+
setuid_gdexdrop = rda_python_miscs.gdexdrop:main
|
|
17
18
|
setuid_gdexkill = rda_python_miscs.gdexkill:main
|
|
18
19
|
setuid_gdexmod = rda_python_miscs.gdexmod:main
|
|
19
20
|
setuid_rdacp = rda_python_miscs.gdexcp:main
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs/rst_templates/index.rst.temp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/requires.txt
RENAMED
|
File without changes
|
{rda_python_miscs-3.0.5 → rda_python_miscs-3.0.6}/src/rda_python_miscs.egg-info/top_level.txt
RENAMED
|
File without changes
|