hid_data_transfer_lib 0.3.2__tar.gz → 0.3.4__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.
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/PKG-INFO +89 -34
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/README.md +88 -33
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/hid_dt_configuration.py +60 -33
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/hid_dt_lib.py +1 -1
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/keycloak/keycloak_rest.py +13 -3
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/nifi/nifi_client.py +14 -5
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/nifi/nifi_rest.py +11 -4
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/pyproject.toml +6 -4
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/__init__.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/__init__.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/hid_dt.cfg +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/hid_dt_local.cfg +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/hid_dt_psnc.cfg +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/exceptions/__init__.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/exceptions/hid_dt_exceptions.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/keycloak/__init__.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/nifi/__init__.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/py.typed +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/util/__init__.py +0 -0
- {hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/util/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: hid_data_transfer_lib
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: HiDALGO Data Transfer library provides methods to transfer data between different data providers and consumers using NIFI pipelines
|
|
5
5
|
License: APL-2.0
|
|
6
6
|
Author: Jesús Gorroñogoitia
|
|
@@ -29,8 +29,8 @@ This library is planning to support the following features:
|
|
|
29
29
|
- transfer datasets from/to local filesystem to/from HPC
|
|
30
30
|
- transfer datasets from/to local filesystem to/from CKAN
|
|
31
31
|
|
|
32
|
-
##
|
|
33
|
-
Current
|
|
32
|
+
## Current version
|
|
33
|
+
Current version of the library supports the following features:
|
|
34
34
|
- transfer datasets from/to Hadoop HDFS to/from HPC
|
|
35
35
|
- transfer datasets from/to Hadoop HDFS to/from CKAN
|
|
36
36
|
- transfer datasets from/to a CKAN to/from HPC
|
|
@@ -41,7 +41,7 @@ Current prototype of the library supports the following features:
|
|
|
41
41
|
This is a Python library that offers specialized API methods to transfer data from data sources to targets.
|
|
42
42
|
Each API method launches a NIFI pipeline, by instantiating a NIFI process group out of its workflow definition registered in the NIFI registry.
|
|
43
43
|
It uses the parameters given within the library method invocation to populate a NIFI parameter context that is asociated to the process group.
|
|
44
|
-
Then, processors in the process group are executed once (or until the incomining processor's flowfile queue gets empty), one after another, following the group sequence flow, until the flow is completed.
|
|
44
|
+
Then, processors in the process group are executed once (or forever until the incomining processor's flowfile queue gets empty), one after another, following the group sequence flow, until the flow is completed.
|
|
45
45
|
A processor is executed after the previous one has terminated. To check the status of the transfer command, the library offers another check-status command.
|
|
46
46
|
Upon termination, the NIFI environment is cleaned up, by removing the created entities (i.e. the process group and its paramenter context).
|
|
47
47
|
The Data Transfer Library sends requests to NIFI through its REST API.
|
|
@@ -50,8 +50,9 @@ The Data Transfer Library sends requests to NIFI through its REST API.
|
|
|
50
50
|
To use the Data Transfer library, it is required the following requirements:
|
|
51
51
|
- **Python3** execution environment
|
|
52
52
|
- **Poetry** python package management tool (optional)
|
|
53
|
-
- **NIFI** instance,
|
|
54
|
-
- **
|
|
53
|
+
- **NIFI** instance, with a NIFI server SSH account (for keys transfer)
|
|
54
|
+
- **Keycloak** instance, with a KEYCLOAK user's account
|
|
55
|
+
- **HDFS** instance, with an user's Kerberos principal account
|
|
55
56
|
- **CKAN** instance, with an user APIKey
|
|
56
57
|
|
|
57
58
|
Python3 should be installed in the computer where Data Transfer CLI will be used.
|
|
@@ -59,7 +60,7 @@ To use the Data Transfer library, it is required the following requirements:
|
|
|
59
60
|
|
|
60
61
|
## Data Transfer lib configuration
|
|
61
62
|
### Configuration file
|
|
62
|
-
Before using the Data Transfer library, you should configure it to point at the target NIFI. The configuration file is located, by default, at the *data_transfer_cli/conf/hid_dt.cfg* file.
|
|
63
|
+
Before using the Data Transfer library, you should configure it to point at the target NIFI and Keycloak services. The default configuration file is located, by default, at the *data_transfer_cli/conf/hid_dt.cfg* file. However, this default configuration should be complemented (and optionally overriden) with user's specific settings, placed in a configuration file whose location should be also specified (e.g., *~/.dtcli/dtcli.cfg*). Settings in this latter user's configuration will override those in the former default library configuration. The user should not modify the default library configuration, but the the user's specific one, including therein any additional required settings (see below) or modifications to the default ones. The location of this user's specific configuration file is passed as parameter to the library when setting it programmatically.
|
|
63
64
|
|
|
64
65
|
```
|
|
65
66
|
[Nifi]
|
|
@@ -73,76 +74,119 @@ keycloak_endpoint=https://idm.hidalgo2.eu
|
|
|
73
74
|
keycloak_client_id=nifi
|
|
74
75
|
keycloak_client_secret=<keycloak_nifi_client_secret>
|
|
75
76
|
|
|
77
|
+
[Logging]
|
|
78
|
+
logging_level=INFO
|
|
79
|
+
|
|
76
80
|
[Network]
|
|
77
81
|
check_status_sleep_lapse=5
|
|
78
82
|
```
|
|
83
|
+
|
|
79
84
|
Under the NIFI section,
|
|
80
85
|
- We define the url of the NIFI service (*nifi_endpoint*),
|
|
81
86
|
- We also specify a folder (*nifi_upload_folder*) in NIFI server where to upload files
|
|
82
87
|
- And another folder (*nifi_download_folder*) where from to download files. These folder must be accessible by the NIFI service (ask NIFI administrator for details).
|
|
83
88
|
- Additionally, you cat set if NIFI servers listens on a secure HTTPS connection (*nifi_secure_connection*=True) or on a non-secure HTTP (*nifi_secure_connection*=False)
|
|
84
89
|
|
|
90
|
+
These default library settings works with the HiDALGO2 NIFI, so not additional modifications are required.
|
|
91
|
+
|
|
85
92
|
Under the Keycloak section, you can configure the Keycloak integrated with NIFI, specifying:
|
|
86
93
|
- The Keycloak service endpoint (*keycloak_endpoint*)
|
|
87
94
|
- The NIFI client in Keycloak (*keycloak_client*)
|
|
88
|
-
- The NIFI secret in Keycloak (*keycloak_client_secret*)
|
|
95
|
+
- The NIFI secret in Keycloak (*keycloak_client_secret*). This setting must be set in the user's configuration (e.g., *~/.dtcli/dtcli.cfg*).
|
|
96
|
+
|
|
97
|
+
These default library settings works with the HiDALGO2 Keycloak, so not additional modifications are required, excepting for the NIFI client secret.
|
|
98
|
+
|
|
99
|
+
Under the Logging section, you can configure the logging level. Logfile *dtcli.log" is located at the workdir of the process that executes the library.
|
|
89
100
|
|
|
90
|
-
Under the Network section, you can configure the lapse time (in seconds) each processor in the NIFI pipeline is checked for
|
|
101
|
+
Under the Network section, you can configure the lapse time (in seconds) each processor in the NIFI pipeline is checked for completion. Most of users should leave the default value.
|
|
102
|
+
|
|
103
|
+
Remember that any modification or addition for the default settings must be placed on the user's specific configuration file (e.g., *~/.dtcli/dtcli.cfg*).
|
|
91
104
|
|
|
92
105
|
HiDALGO2 developers can contact the Keycloak administrator for the *keycloak_client_secret*
|
|
93
106
|
|
|
94
|
-
### User's accounts
|
|
107
|
+
### User's accounts
|
|
95
108
|
|
|
96
|
-
|
|
97
|
-
- NIFI_SERVER_USERNAME: `export NIFI_SERVER_USERNAME=<nifi_server_username>`
|
|
98
|
-
- NIFI_SERVER_PRIVATE_KEY: `export NIFI_SERVER_PRIVATE_KEY=<path_to_private_key>`
|
|
109
|
+
Additional user's accounts are specified in the user's specific configuration file (e.g., *~/.dtcli/dtcli.cfg*):
|
|
99
110
|
|
|
100
|
-
|
|
111
|
+
```
|
|
112
|
+
[Nifi]
|
|
113
|
+
nifi_server_username=<user_name>
|
|
114
|
+
nifi_server_private_key=<path/to/private/key>
|
|
101
115
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
[Keycloak]
|
|
117
|
+
keycloak_login=<user_name>
|
|
118
|
+
keycloak_password=<password>
|
|
119
|
+
```
|
|
106
120
|
|
|
107
|
-
|
|
121
|
+
Under the Nifi section, you must also specify a user account (username, private_key) that grants to upload/download files to the NIFI server (as requested to upload temporary HPC keys or to support local file transfer). This user's account is provided by Hidalgo2 infrastructure provider and it is user's or service's specific.
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
The Keycloak account must be configured in the following environment variables:
|
|
111
|
-
- KEYCLOAK_LOGIN: `export KEYCLOAK_LOGIN=<keycloak_login>`
|
|
112
|
-
- KEYCLOAK_PASSWORD: `export KEYCLOAK_PASSWORD=<keycloak_password>`
|
|
123
|
+
Under the Keycloak section, you must specify your Keycloak account (username and password). This account granted with access to the NIFI service.
|
|
113
124
|
|
|
114
125
|
For HiDALGO2 developers, NIFI (Service, Server) and Keycloak accounts are provided by the HiDALGO2 administrator.
|
|
115
126
|
|
|
116
127
|
|
|
117
128
|
## Usage
|
|
118
|
-
The data transfer library can be invoked following
|
|
129
|
+
The data transfer library can be invoked following two procedures:
|
|
119
130
|
|
|
120
|
-
|
|
131
|
+
### Using user's configuration (e.g., ~/.dtcli/dtcli.cfg)
|
|
132
|
+
In this case, user's configuration will be read from a give file, such as *~/.dtcli/dtcli.cfg*, whose location is programmatically passed as a parameter upon the setup of the library (see procedure below).
|
|
133
|
+
|
|
134
|
+
### Providing configuration in a dictionary
|
|
135
|
+
In this case, the user's configuration is provided in a dictionary, with this structure:
|
|
121
136
|
```
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
{
|
|
138
|
+
'Nifi':
|
|
139
|
+
{
|
|
140
|
+
'nifi_server_username': '<username>',
|
|
141
|
+
'nifi_server_private_key': '<path/to/private/key>'
|
|
142
|
+
},
|
|
143
|
+
'Keycloak':
|
|
144
|
+
{
|
|
145
|
+
'keycloak_login': '<username>',
|
|
146
|
+
'keycloak_password': '<password>',
|
|
147
|
+
'keycloak_client_secret': '<client_secret>'
|
|
148
|
+
},
|
|
149
|
+
'Logging': {
|
|
150
|
+
'logging_level': 'DEBUG'},
|
|
151
|
+
'Network': {
|
|
152
|
+
'check_status_sleep_lapse': '2'
|
|
153
|
+
}
|
|
154
|
+
}
|
|
126
155
|
```
|
|
127
|
-
|
|
128
|
-
|
|
156
|
+
In this settings' dictionary you should add the user's specific accounts for Nifi and Keycloak, and optionally, other settings, as shown for the logging level or the sleep lapse time for checking the processors status on the Nifi pipeline. This dictionary is programmatically passed as parameter to the library upon its setup (see procedure below).
|
|
157
|
+
|
|
158
|
+
The remaining procedure goes as follows:
|
|
129
159
|
|
|
130
160
|
- In your python code, instantiate a HIDDataTransferConfiguration object and an HIDDataTranfer object
|
|
131
|
-
The HDIDataTransfer object can be created, by default, using the
|
|
161
|
+
The HDIDataTransfer object can be created, by default, using the configuration read from the user's configuration file (or from the provided configuration dictionary),
|
|
132
162
|
or by providing a dictionary with the Keycloak token, the refresh token, and the expiration time
|
|
133
163
|
|
|
164
|
+
Example with user's configuration file
|
|
165
|
+
|
|
134
166
|
```
|
|
135
167
|
from hid_data_transfer_lib.hid_dt_lib import HIDDataTransfer
|
|
136
168
|
from hid_data_transfer_lib.conf.hid_dt_configuration import (
|
|
137
169
|
HidDataTransferConfiguration
|
|
138
170
|
)
|
|
139
171
|
|
|
140
|
-
|
|
141
|
-
|
|
172
|
+
# Using Keycloak configuration from users's file
|
|
173
|
+
user_config_file = None
|
|
174
|
+
if os.path.exists(os.path.expanduser("~/.dtcli/dtcli.cfg")):
|
|
175
|
+
user_config_file = os.path.expanduser("~/.dtcli/dtcli.cfg")
|
|
176
|
+
config = HidDataTransferConfiguration().configure(
|
|
177
|
+
user_config_file=user_config_file,
|
|
178
|
+
logging_level=logging.DEBUG)
|
|
179
|
+
# Create a HIDDataTransfer object that uses the Keycloak user's configuration
|
|
142
180
|
dt_client = HIDDataTransfer(conf=config, secure=True)
|
|
143
181
|
|
|
144
182
|
# OR
|
|
145
183
|
|
|
184
|
+
user_config_file = None
|
|
185
|
+
if os.path.exists(os.path.expanduser("~/.dtcli/dtcli.cfg")):
|
|
186
|
+
user_config_file = os.path.expanduser("~/.dtcli/dtcli.cfg")
|
|
187
|
+
config = HidDataTransferConfiguration().configure(
|
|
188
|
+
user_config_file=user_config_file,
|
|
189
|
+
logging_level=logging.DEBUG)
|
|
146
190
|
# Create a HIDDataTransfer object that uses the provided Keycloak token dictionary
|
|
147
191
|
keycloak_token = {
|
|
148
192
|
"username": <keycloak_username>,
|
|
@@ -156,6 +200,17 @@ dt_client = HIDDataTransfer(
|
|
|
156
200
|
keycloak_token=keycloak_token
|
|
157
201
|
)
|
|
158
202
|
```
|
|
203
|
+
|
|
204
|
+
Example with user's configuration dictionary:
|
|
205
|
+
```
|
|
206
|
+
user_config_dict = {...} # See example of dictionary given above
|
|
207
|
+
config = HidDataTransferConfiguration().configure(
|
|
208
|
+
user_config_dict=user_config_dict,
|
|
209
|
+
logging_level=logging.DEBUG)
|
|
210
|
+
dt_client = HIDDataTransfer(conf=config, secure=True)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
|
|
159
214
|
- Invoke any data transfer library method using the created object to tranfer data
|
|
160
215
|
```
|
|
161
216
|
dt_client.ckan2hpc(
|
|
@@ -11,8 +11,8 @@ This library is planning to support the following features:
|
|
|
11
11
|
- transfer datasets from/to local filesystem to/from HPC
|
|
12
12
|
- transfer datasets from/to local filesystem to/from CKAN
|
|
13
13
|
|
|
14
|
-
##
|
|
15
|
-
Current
|
|
14
|
+
## Current version
|
|
15
|
+
Current version of the library supports the following features:
|
|
16
16
|
- transfer datasets from/to Hadoop HDFS to/from HPC
|
|
17
17
|
- transfer datasets from/to Hadoop HDFS to/from CKAN
|
|
18
18
|
- transfer datasets from/to a CKAN to/from HPC
|
|
@@ -23,7 +23,7 @@ Current prototype of the library supports the following features:
|
|
|
23
23
|
This is a Python library that offers specialized API methods to transfer data from data sources to targets.
|
|
24
24
|
Each API method launches a NIFI pipeline, by instantiating a NIFI process group out of its workflow definition registered in the NIFI registry.
|
|
25
25
|
It uses the parameters given within the library method invocation to populate a NIFI parameter context that is asociated to the process group.
|
|
26
|
-
Then, processors in the process group are executed once (or until the incomining processor's flowfile queue gets empty), one after another, following the group sequence flow, until the flow is completed.
|
|
26
|
+
Then, processors in the process group are executed once (or forever until the incomining processor's flowfile queue gets empty), one after another, following the group sequence flow, until the flow is completed.
|
|
27
27
|
A processor is executed after the previous one has terminated. To check the status of the transfer command, the library offers another check-status command.
|
|
28
28
|
Upon termination, the NIFI environment is cleaned up, by removing the created entities (i.e. the process group and its paramenter context).
|
|
29
29
|
The Data Transfer Library sends requests to NIFI through its REST API.
|
|
@@ -32,8 +32,9 @@ The Data Transfer Library sends requests to NIFI through its REST API.
|
|
|
32
32
|
To use the Data Transfer library, it is required the following requirements:
|
|
33
33
|
- **Python3** execution environment
|
|
34
34
|
- **Poetry** python package management tool (optional)
|
|
35
|
-
- **NIFI** instance,
|
|
36
|
-
- **
|
|
35
|
+
- **NIFI** instance, with a NIFI server SSH account (for keys transfer)
|
|
36
|
+
- **Keycloak** instance, with a KEYCLOAK user's account
|
|
37
|
+
- **HDFS** instance, with an user's Kerberos principal account
|
|
37
38
|
- **CKAN** instance, with an user APIKey
|
|
38
39
|
|
|
39
40
|
Python3 should be installed in the computer where Data Transfer CLI will be used.
|
|
@@ -41,7 +42,7 @@ To use the Data Transfer library, it is required the following requirements:
|
|
|
41
42
|
|
|
42
43
|
## Data Transfer lib configuration
|
|
43
44
|
### Configuration file
|
|
44
|
-
Before using the Data Transfer library, you should configure it to point at the target NIFI. The configuration file is located, by default, at the *data_transfer_cli/conf/hid_dt.cfg* file.
|
|
45
|
+
Before using the Data Transfer library, you should configure it to point at the target NIFI and Keycloak services. The default configuration file is located, by default, at the *data_transfer_cli/conf/hid_dt.cfg* file. However, this default configuration should be complemented (and optionally overriden) with user's specific settings, placed in a configuration file whose location should be also specified (e.g., *~/.dtcli/dtcli.cfg*). Settings in this latter user's configuration will override those in the former default library configuration. The user should not modify the default library configuration, but the the user's specific one, including therein any additional required settings (see below) or modifications to the default ones. The location of this user's specific configuration file is passed as parameter to the library when setting it programmatically.
|
|
45
46
|
|
|
46
47
|
```
|
|
47
48
|
[Nifi]
|
|
@@ -55,76 +56,119 @@ keycloak_endpoint=https://idm.hidalgo2.eu
|
|
|
55
56
|
keycloak_client_id=nifi
|
|
56
57
|
keycloak_client_secret=<keycloak_nifi_client_secret>
|
|
57
58
|
|
|
59
|
+
[Logging]
|
|
60
|
+
logging_level=INFO
|
|
61
|
+
|
|
58
62
|
[Network]
|
|
59
63
|
check_status_sleep_lapse=5
|
|
60
64
|
```
|
|
65
|
+
|
|
61
66
|
Under the NIFI section,
|
|
62
67
|
- We define the url of the NIFI service (*nifi_endpoint*),
|
|
63
68
|
- We also specify a folder (*nifi_upload_folder*) in NIFI server where to upload files
|
|
64
69
|
- And another folder (*nifi_download_folder*) where from to download files. These folder must be accessible by the NIFI service (ask NIFI administrator for details).
|
|
65
70
|
- Additionally, you cat set if NIFI servers listens on a secure HTTPS connection (*nifi_secure_connection*=True) or on a non-secure HTTP (*nifi_secure_connection*=False)
|
|
66
71
|
|
|
72
|
+
These default library settings works with the HiDALGO2 NIFI, so not additional modifications are required.
|
|
73
|
+
|
|
67
74
|
Under the Keycloak section, you can configure the Keycloak integrated with NIFI, specifying:
|
|
68
75
|
- The Keycloak service endpoint (*keycloak_endpoint*)
|
|
69
76
|
- The NIFI client in Keycloak (*keycloak_client*)
|
|
70
|
-
- The NIFI secret in Keycloak (*keycloak_client_secret*)
|
|
77
|
+
- The NIFI secret in Keycloak (*keycloak_client_secret*). This setting must be set in the user's configuration (e.g., *~/.dtcli/dtcli.cfg*).
|
|
78
|
+
|
|
79
|
+
These default library settings works with the HiDALGO2 Keycloak, so not additional modifications are required, excepting for the NIFI client secret.
|
|
80
|
+
|
|
81
|
+
Under the Logging section, you can configure the logging level. Logfile *dtcli.log" is located at the workdir of the process that executes the library.
|
|
71
82
|
|
|
72
|
-
Under the Network section, you can configure the lapse time (in seconds) each processor in the NIFI pipeline is checked for
|
|
83
|
+
Under the Network section, you can configure the lapse time (in seconds) each processor in the NIFI pipeline is checked for completion. Most of users should leave the default value.
|
|
84
|
+
|
|
85
|
+
Remember that any modification or addition for the default settings must be placed on the user's specific configuration file (e.g., *~/.dtcli/dtcli.cfg*).
|
|
73
86
|
|
|
74
87
|
HiDALGO2 developers can contact the Keycloak administrator for the *keycloak_client_secret*
|
|
75
88
|
|
|
76
|
-
### User's accounts
|
|
89
|
+
### User's accounts
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
- NIFI_SERVER_USERNAME: `export NIFI_SERVER_USERNAME=<nifi_server_username>`
|
|
80
|
-
- NIFI_SERVER_PRIVATE_KEY: `export NIFI_SERVER_PRIVATE_KEY=<path_to_private_key>`
|
|
91
|
+
Additional user's accounts are specified in the user's specific configuration file (e.g., *~/.dtcli/dtcli.cfg*):
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
```
|
|
94
|
+
[Nifi]
|
|
95
|
+
nifi_server_username=<user_name>
|
|
96
|
+
nifi_server_private_key=<path/to/private/key>
|
|
83
97
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
[Keycloak]
|
|
99
|
+
keycloak_login=<user_name>
|
|
100
|
+
keycloak_password=<password>
|
|
101
|
+
```
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
Under the Nifi section, you must also specify a user account (username, private_key) that grants to upload/download files to the NIFI server (as requested to upload temporary HPC keys or to support local file transfer). This user's account is provided by Hidalgo2 infrastructure provider and it is user's or service's specific.
|
|
90
104
|
|
|
91
|
-
|
|
92
|
-
The Keycloak account must be configured in the following environment variables:
|
|
93
|
-
- KEYCLOAK_LOGIN: `export KEYCLOAK_LOGIN=<keycloak_login>`
|
|
94
|
-
- KEYCLOAK_PASSWORD: `export KEYCLOAK_PASSWORD=<keycloak_password>`
|
|
105
|
+
Under the Keycloak section, you must specify your Keycloak account (username and password). This account granted with access to the NIFI service.
|
|
95
106
|
|
|
96
107
|
For HiDALGO2 developers, NIFI (Service, Server) and Keycloak accounts are provided by the HiDALGO2 administrator.
|
|
97
108
|
|
|
98
109
|
|
|
99
110
|
## Usage
|
|
100
|
-
The data transfer library can be invoked following
|
|
111
|
+
The data transfer library can be invoked following two procedures:
|
|
101
112
|
|
|
102
|
-
|
|
113
|
+
### Using user's configuration (e.g., ~/.dtcli/dtcli.cfg)
|
|
114
|
+
In this case, user's configuration will be read from a give file, such as *~/.dtcli/dtcli.cfg*, whose location is programmatically passed as a parameter upon the setup of the library (see procedure below).
|
|
115
|
+
|
|
116
|
+
### Providing configuration in a dictionary
|
|
117
|
+
In this case, the user's configuration is provided in a dictionary, with this structure:
|
|
103
118
|
```
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
{
|
|
120
|
+
'Nifi':
|
|
121
|
+
{
|
|
122
|
+
'nifi_server_username': '<username>',
|
|
123
|
+
'nifi_server_private_key': '<path/to/private/key>'
|
|
124
|
+
},
|
|
125
|
+
'Keycloak':
|
|
126
|
+
{
|
|
127
|
+
'keycloak_login': '<username>',
|
|
128
|
+
'keycloak_password': '<password>',
|
|
129
|
+
'keycloak_client_secret': '<client_secret>'
|
|
130
|
+
},
|
|
131
|
+
'Logging': {
|
|
132
|
+
'logging_level': 'DEBUG'},
|
|
133
|
+
'Network': {
|
|
134
|
+
'check_status_sleep_lapse': '2'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
108
137
|
```
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
In this settings' dictionary you should add the user's specific accounts for Nifi and Keycloak, and optionally, other settings, as shown for the logging level or the sleep lapse time for checking the processors status on the Nifi pipeline. This dictionary is programmatically passed as parameter to the library upon its setup (see procedure below).
|
|
139
|
+
|
|
140
|
+
The remaining procedure goes as follows:
|
|
111
141
|
|
|
112
142
|
- In your python code, instantiate a HIDDataTransferConfiguration object and an HIDDataTranfer object
|
|
113
|
-
The HDIDataTransfer object can be created, by default, using the
|
|
143
|
+
The HDIDataTransfer object can be created, by default, using the configuration read from the user's configuration file (or from the provided configuration dictionary),
|
|
114
144
|
or by providing a dictionary with the Keycloak token, the refresh token, and the expiration time
|
|
115
145
|
|
|
146
|
+
Example with user's configuration file
|
|
147
|
+
|
|
116
148
|
```
|
|
117
149
|
from hid_data_transfer_lib.hid_dt_lib import HIDDataTransfer
|
|
118
150
|
from hid_data_transfer_lib.conf.hid_dt_configuration import (
|
|
119
151
|
HidDataTransferConfiguration
|
|
120
152
|
)
|
|
121
153
|
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
# Using Keycloak configuration from users's file
|
|
155
|
+
user_config_file = None
|
|
156
|
+
if os.path.exists(os.path.expanduser("~/.dtcli/dtcli.cfg")):
|
|
157
|
+
user_config_file = os.path.expanduser("~/.dtcli/dtcli.cfg")
|
|
158
|
+
config = HidDataTransferConfiguration().configure(
|
|
159
|
+
user_config_file=user_config_file,
|
|
160
|
+
logging_level=logging.DEBUG)
|
|
161
|
+
# Create a HIDDataTransfer object that uses the Keycloak user's configuration
|
|
124
162
|
dt_client = HIDDataTransfer(conf=config, secure=True)
|
|
125
163
|
|
|
126
164
|
# OR
|
|
127
165
|
|
|
166
|
+
user_config_file = None
|
|
167
|
+
if os.path.exists(os.path.expanduser("~/.dtcli/dtcli.cfg")):
|
|
168
|
+
user_config_file = os.path.expanduser("~/.dtcli/dtcli.cfg")
|
|
169
|
+
config = HidDataTransferConfiguration().configure(
|
|
170
|
+
user_config_file=user_config_file,
|
|
171
|
+
logging_level=logging.DEBUG)
|
|
128
172
|
# Create a HIDDataTransfer object that uses the provided Keycloak token dictionary
|
|
129
173
|
keycloak_token = {
|
|
130
174
|
"username": <keycloak_username>,
|
|
@@ -138,6 +182,17 @@ dt_client = HIDDataTransfer(
|
|
|
138
182
|
keycloak_token=keycloak_token
|
|
139
183
|
)
|
|
140
184
|
```
|
|
185
|
+
|
|
186
|
+
Example with user's configuration dictionary:
|
|
187
|
+
```
|
|
188
|
+
user_config_dict = {...} # See example of dictionary given above
|
|
189
|
+
config = HidDataTransferConfiguration().configure(
|
|
190
|
+
user_config_dict=user_config_dict,
|
|
191
|
+
logging_level=logging.DEBUG)
|
|
192
|
+
dt_client = HIDDataTransfer(conf=config, secure=True)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
|
|
141
196
|
- Invoke any data transfer library method using the created object to tranfer data
|
|
142
197
|
```
|
|
143
198
|
dt_client.ckan2hpc(
|
|
@@ -14,7 +14,7 @@ limitations under the License.
|
|
|
14
14
|
|
|
15
15
|
This module manages the configuration of the Data Transfer CLI
|
|
16
16
|
"""
|
|
17
|
-
|
|
17
|
+
from __future__ import annotations
|
|
18
18
|
import configparser
|
|
19
19
|
import os
|
|
20
20
|
import logging
|
|
@@ -45,8 +45,8 @@ class HidDataTransferConfiguration:
|
|
|
45
45
|
|
|
46
46
|
__check_status_sleep_lapse = 10 # seconds
|
|
47
47
|
|
|
48
|
-
def __init__(self, logging_level: Optional[int] =
|
|
49
|
-
self.
|
|
48
|
+
def __init__(self, logging_level: Optional[int] = logging.INFO):
|
|
49
|
+
self.__logging_level = logging_level
|
|
50
50
|
|
|
51
51
|
def check_keycloak_conf(self):
|
|
52
52
|
"""Check if keycloak configuration is valid"""
|
|
@@ -210,27 +210,61 @@ class HidDataTransferConfiguration:
|
|
|
210
210
|
if isinstance(handler, logging.FileHandler):
|
|
211
211
|
return handler
|
|
212
212
|
return None
|
|
213
|
-
|
|
213
|
+
|
|
214
214
|
def check_status_sleep_lapse(self):
|
|
215
215
|
"""Returns the NIFI login name"""
|
|
216
216
|
return self.__check_status_sleep_lapse
|
|
217
217
|
|
|
218
|
-
def
|
|
218
|
+
def read_configuration(
|
|
219
|
+
self,
|
|
220
|
+
user_config_file: Optional[str] = None,
|
|
221
|
+
user_config_dict: Optional[dict] = None
|
|
222
|
+
) -> configparser.RawConfigParser:
|
|
223
|
+
"""
|
|
224
|
+
Reads the configuration file and returns a configparser object.
|
|
219
225
|
"""
|
|
220
|
-
Reads the configuration file and sets this class' configuration values
|
|
221
226
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
if user_config_file is not None and user_config_dict is not None:
|
|
228
|
+
raise HidDataTransferException(
|
|
229
|
+
(
|
|
230
|
+
"Only one of user_config_file or user_config_dict "
|
|
231
|
+
"should be provided"
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
if user_config_file is None and user_config_dict is None:
|
|
235
|
+
raise HidDataTransferException(
|
|
236
|
+
"One of user_config_file or user_config_dict "
|
|
237
|
+
"should be provided"
|
|
238
|
+
)
|
|
226
239
|
|
|
227
|
-
"""
|
|
228
240
|
config = configparser.RawConfigParser()
|
|
229
|
-
config_file = os.
|
|
230
|
-
|
|
231
|
-
config_file = str(os.path.dirname(os.path.realpath(__file__))) + \
|
|
232
|
-
"/hid_dt.cfg"
|
|
241
|
+
config_file = str(os.path.dirname(os.path.realpath(__file__))) + \
|
|
242
|
+
"/hid_dt.cfg"
|
|
233
243
|
config.read(config_file)
|
|
244
|
+
if user_config_file is not None:
|
|
245
|
+
config.read(user_config_file)
|
|
246
|
+
if user_config_dict is not None:
|
|
247
|
+
config.read_dict(user_config_dict)
|
|
248
|
+
return config
|
|
249
|
+
|
|
250
|
+
def configure(
|
|
251
|
+
self,
|
|
252
|
+
user_config_file: Optional[str] = None,
|
|
253
|
+
user_config_dict: Optional[dict] = None,
|
|
254
|
+
logging_level: Optional[int] = None
|
|
255
|
+
) -> HidDataTransferConfiguration:
|
|
256
|
+
"""
|
|
257
|
+
Reads the configuration and sets this class' configuration values
|
|
258
|
+
It reads the configuration
|
|
259
|
+
from the default library configuration directory.
|
|
260
|
+
If the user_config_file is provided, this files is read, and its values
|
|
261
|
+
override those in the default configuration file.
|
|
262
|
+
If the user_config_dict is provided, this dictionary is read,
|
|
263
|
+
and its values override those in the default configuration file.
|
|
264
|
+
Only one of user_config_file or user_config_dict should be provided.
|
|
265
|
+
|
|
266
|
+
"""
|
|
267
|
+
config = self.read_configuration(user_config_file, user_config_dict)
|
|
234
268
|
|
|
235
269
|
try:
|
|
236
270
|
# NIFI section
|
|
@@ -257,11 +291,13 @@ class HidDataTransferConfiguration:
|
|
|
257
291
|
logging_level_str = config.get("Logging", "logging_level")
|
|
258
292
|
self.__logging_level = getattr(
|
|
259
293
|
logging, logging_level_str.upper(), logging.INFO)
|
|
260
|
-
|
|
294
|
+
|
|
261
295
|
# Network section
|
|
262
296
|
self.__check_status_sleep_lapse = config.get(
|
|
263
297
|
"Network", "check_status_sleep_lapse")
|
|
264
298
|
|
|
299
|
+
return self
|
|
300
|
+
|
|
265
301
|
except configparser.NoSectionError as ex:
|
|
266
302
|
raise HidDataTransferException(
|
|
267
303
|
"Error parsing CLI configuration file"
|
|
@@ -269,9 +305,9 @@ class HidDataTransferConfiguration:
|
|
|
269
305
|
|
|
270
306
|
def load_keycloak_configuration(self, config):
|
|
271
307
|
"""Load Keycloak configuration from config file"""
|
|
272
|
-
# Read Keycloak login and password from
|
|
273
|
-
self.__keycloak_login =
|
|
274
|
-
self.__keycloak_passwd =
|
|
308
|
+
# Read Keycloak login and password from configuration
|
|
309
|
+
self.__keycloak_login = config.get("Keycloak", "keycloak_login")
|
|
310
|
+
self.__keycloak_passwd = config.get("Keycloak", "keycloak_password")
|
|
275
311
|
if (self.__keycloak_login and self.__keycloak_passwd is None) or (
|
|
276
312
|
self.__keycloak_login is None and self.__keycloak_passwd
|
|
277
313
|
):
|
|
@@ -315,17 +351,6 @@ class HidDataTransferConfiguration:
|
|
|
315
351
|
"Could not find Nifi endpoint in config file."
|
|
316
352
|
)
|
|
317
353
|
|
|
318
|
-
# Read Nifi login and password from environment variables
|
|
319
|
-
self.__nifi_login = os.getenv("NIFI_LOGIN")
|
|
320
|
-
self.__nifi_passwd = os.getenv("NIFI_PASSWORD")
|
|
321
|
-
if (self.__nifi_login and self.__nifi_passwd is None) or (
|
|
322
|
-
self.__nifi_login is None and self.__nifi_passwd
|
|
323
|
-
):
|
|
324
|
-
raise HidDataTransferException(
|
|
325
|
-
"Both NIFI_LOGIN and NIFI_PASSWORD "
|
|
326
|
-
"environment variables must be set"
|
|
327
|
-
)
|
|
328
|
-
|
|
329
354
|
nifi_secure_connection = config.get("Nifi", "nifi_secure_connection")
|
|
330
355
|
if nifi_secure_connection is not None:
|
|
331
356
|
self.__nifi_secure_connection = \
|
|
@@ -346,9 +371,11 @@ class HidDataTransferConfiguration:
|
|
|
346
371
|
"Could not find Nifi nifi_download_folder in config file"
|
|
347
372
|
)
|
|
348
373
|
|
|
349
|
-
|
|
350
|
-
self.__nifi_server_user_name =
|
|
351
|
-
|
|
374
|
+
# Optional NIFI server account in case of local file transfer
|
|
375
|
+
self.__nifi_server_user_name = config.get(
|
|
376
|
+
"Nifi", "nifi_server_username")
|
|
377
|
+
self.__nifi_server_private_key = config.get(
|
|
378
|
+
"Nifi", "nifi_server_private_key")
|
|
352
379
|
|
|
353
380
|
if (
|
|
354
381
|
self.__nifi_server_user_name and
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/hid_dt_lib.py
RENAMED
|
@@ -65,7 +65,7 @@ class HIDDataTransfer:
|
|
|
65
65
|
with the NIFI endpoint taken from configuration
|
|
66
66
|
"""
|
|
67
67
|
self.__conf = conf
|
|
68
|
-
self.__nifi_client = NIFIClient(conf, secure)
|
|
68
|
+
self.__nifi_client = NIFIClient().configure(conf, secure)
|
|
69
69
|
self.__logger = self.__conf.logger("hid_data_transfer_lib")
|
|
70
70
|
self.__keycloak_token = keycloak_token
|
|
71
71
|
|
|
@@ -16,7 +16,7 @@ This module defines the Keycloak API client class.
|
|
|
16
16
|
It provides methods to interface the Keycloak server to
|
|
17
17
|
authenticate users and retrieve tokens.
|
|
18
18
|
"""
|
|
19
|
-
|
|
19
|
+
from __future__ import annotations
|
|
20
20
|
from typing import Optional
|
|
21
21
|
from urllib.parse import urlparse, urlunparse, quote_plus
|
|
22
22
|
import threading
|
|
@@ -58,10 +58,19 @@ class KeycloakRESTClient:
|
|
|
58
58
|
__expires_in = None
|
|
59
59
|
__refresh_timer = None
|
|
60
60
|
|
|
61
|
-
def __init__(
|
|
61
|
+
def __init__(self):
|
|
62
|
+
"""Constructor method"""
|
|
63
|
+
self.__conf = None
|
|
64
|
+
self.__keycloak_login = None
|
|
65
|
+
self.__keycloak_endpoint = None
|
|
66
|
+
self.__logger = None
|
|
67
|
+
self.__refresh = False
|
|
68
|
+
self.__refresh_timer = None
|
|
69
|
+
|
|
70
|
+
def configure(
|
|
62
71
|
self, conf: HidDataTransferConfiguration,
|
|
63
72
|
secure: bool = False, refresh: bool = False
|
|
64
|
-
) ->
|
|
73
|
+
) -> KeycloakRESTClient:
|
|
65
74
|
"""constructs a API client,
|
|
66
75
|
with the Keycloak endpoint taken from configuration
|
|
67
76
|
"""
|
|
@@ -73,6 +82,7 @@ class KeycloakRESTClient:
|
|
|
73
82
|
self.__conf.keycloak_endpoint(), secure
|
|
74
83
|
)
|
|
75
84
|
self.__logger = self.__conf.logger("keycloak.keycloak_api")
|
|
85
|
+
return self
|
|
76
86
|
|
|
77
87
|
def info(self, *args):
|
|
78
88
|
''' do info logging'''
|
|
@@ -17,7 +17,7 @@ This module defines the NIFI API client class.
|
|
|
17
17
|
It provides methods to interface the NIFI server to in instantiate templates,
|
|
18
18
|
and run processors in a process group.
|
|
19
19
|
"""
|
|
20
|
-
|
|
20
|
+
from __future__ import annotations
|
|
21
21
|
import glob
|
|
22
22
|
import json
|
|
23
23
|
import os
|
|
@@ -99,18 +99,27 @@ class NIFIClient:
|
|
|
99
99
|
CKAN_RESOURCE = "ckan.resource"
|
|
100
100
|
KERBEROS_PASSWORD = "Kerberos Password"
|
|
101
101
|
|
|
102
|
-
def __init__(self
|
|
103
|
-
|
|
102
|
+
def __init__(self):
|
|
103
|
+
"""Constructor method"""
|
|
104
|
+
self.__conf = None
|
|
105
|
+
self.nifi_rest_client = None
|
|
106
|
+
self.keycloak_rest_client = None
|
|
107
|
+
self.__logger = None
|
|
108
|
+
self.check_status_sleep_lapse = 1
|
|
109
|
+
|
|
110
|
+
def configure(self, conf: HidDataTransferConfiguration,
|
|
111
|
+
secure: bool = False) -> NIFIClient:
|
|
104
112
|
"""constructs a NIFI client,
|
|
105
113
|
with the NIFI endpoint taken from configuration
|
|
106
114
|
"""
|
|
107
115
|
self.__conf = conf
|
|
108
|
-
self.nifi_rest_client = NIFIRESTClient(conf, secure)
|
|
109
|
-
self.keycloak_rest_client = KeycloakRESTClient(
|
|
116
|
+
self.nifi_rest_client = NIFIRESTClient().configure(conf, secure)
|
|
117
|
+
self.keycloak_rest_client = KeycloakRESTClient().configure(
|
|
110
118
|
conf, secure, refresh=True)
|
|
111
119
|
self.__logger = self.__conf.logger("nifi.v2.client")
|
|
112
120
|
self.check_status_sleep_lapse = int(
|
|
113
121
|
self.__conf.check_status_sleep_lapse())
|
|
122
|
+
return self
|
|
114
123
|
|
|
115
124
|
def get_access_token(self) -> str:
|
|
116
125
|
"""gets the NIFI access token,
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/nifi/nifi_rest.py
RENAMED
|
@@ -17,7 +17,7 @@ This module defines the NIFI REST client class.
|
|
|
17
17
|
It provides methods to interface the NIFI server to in instantiate templates,
|
|
18
18
|
and run processors in a process group.
|
|
19
19
|
"""
|
|
20
|
-
|
|
20
|
+
from __future__ import annotations
|
|
21
21
|
import json
|
|
22
22
|
from typing import Optional
|
|
23
23
|
import uuid
|
|
@@ -59,8 +59,13 @@ class NIFIRESTClient:
|
|
|
59
59
|
__JSON_CONTENT_TYPE = "application/json"
|
|
60
60
|
__TEXT_PLAIN_CONTENT_TYPE = "text/plain"
|
|
61
61
|
|
|
62
|
-
def __init__(self
|
|
63
|
-
|
|
62
|
+
def __init__(self):
|
|
63
|
+
"""initializes the NIFI REST client"""
|
|
64
|
+
self.__conf = None
|
|
65
|
+
self.__nifi_endpoint = None
|
|
66
|
+
|
|
67
|
+
def configure(self, conf: HidDataTransferConfiguration,
|
|
68
|
+
secure: bool = False) -> NIFIRESTClient:
|
|
64
69
|
"""constructs a NIFI REST client,
|
|
65
70
|
with the NIFI endpoint taken from configuration
|
|
66
71
|
"""
|
|
@@ -68,6 +73,7 @@ class NIFIRESTClient:
|
|
|
68
73
|
self.__conf = conf
|
|
69
74
|
self.__nifi_endpoint = parse_base_url(
|
|
70
75
|
self.__conf.nifi_endpoint(), secure)
|
|
76
|
+
return self
|
|
71
77
|
|
|
72
78
|
def build_url(self, *paths: str, parameters: Optional[dict] = None) -> str:
|
|
73
79
|
"""constructs a NIFI query endpoint,
|
|
@@ -723,7 +729,8 @@ class NIFIRESTClient:
|
|
|
723
729
|
return response.ok
|
|
724
730
|
else:
|
|
725
731
|
raise HidDataTransferException(
|
|
726
|
-
|
|
732
|
+
"Delete listing request exception: "
|
|
733
|
+
f"{response.content.decode()}"
|
|
727
734
|
)
|
|
728
735
|
|
|
729
736
|
# MULTI-REQUESTS methods
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hid_data_transfer_lib"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.4"
|
|
4
4
|
description = "HiDALGO Data Transfer library provides methods to transfer data between different data providers and consumers using NIFI pipelines"
|
|
5
|
-
authors = [
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Jesús Gorroñogoitia", email = "jesus.gorronogoitia@eviden.com" },
|
|
7
|
+
]
|
|
6
8
|
license = "APL-2.0"
|
|
7
9
|
readme = "README.md"
|
|
8
10
|
requires-python = ">=3.11"
|
|
9
|
-
dependencies= [
|
|
11
|
+
dependencies = [
|
|
10
12
|
"requests>=2.31.0",
|
|
11
13
|
"paramiko>=3.3.1",
|
|
12
|
-
"setuptools (>=79.0.0,<80.0.0)"
|
|
14
|
+
"setuptools (>=79.0.0,<80.0.0)",
|
|
13
15
|
]
|
|
14
16
|
|
|
15
17
|
[tool.poetry]
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/__init__.py
RENAMED
|
File without changes
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/__init__.py
RENAMED
|
File without changes
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/conf/hid_dt.cfg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/nifi/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/util/__init__.py
RENAMED
|
File without changes
|
{hid_data_transfer_lib-0.3.2 → hid_data_transfer_lib-0.3.4}/hid_data_transfer_lib/util/util.py
RENAMED
|
File without changes
|