nost-tools 2.0.0__py3-none-any.whl → 2.0.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.

Potentially problematic release.


This version of nost-tools might be problematic. Click here for more details.

@@ -1,304 +1,304 @@
1
- """
2
- Configuration Settings.
3
- """
4
-
5
- import logging
6
- import os
7
-
8
- import yaml
9
- from dotenv import find_dotenv, load_dotenv
10
- from pydantic import ValidationError
11
-
12
- from .errors import ConfigAssertionError, ConfigurationError, EnvironmentVariableError
13
- from .schemas import (
14
- ChannelConfig,
15
- Config,
16
- Credentials,
17
- ExchangeConfig,
18
- ExecConfig,
19
- KeycloakConfig,
20
- RabbitMQConfig,
21
- RuntimeConfig,
22
- ServersConfig,
23
- SimulationConfig,
24
- )
25
-
26
- logger = logging.getLogger(__name__)
27
-
28
- class ConnectionConfig:
29
- """Connection configuration.
30
-
31
- The configuration settings to establish a connection to the broker, including authentication for the
32
- user and identification of the server.
33
-
34
- Attributes:
35
- username (str): client username, provided by NOS-T operator
36
- password (str): client password, provided by NOS-T operator
37
- host (str): broker hostname
38
- rabbitmq_port (int): RabbitMQ broker port number
39
- keycloak_port (int): Keycloak IAM port number
40
- keycloak_realm (str): Keycloak realm name
41
- client_id (str): Keycloak client ID
42
- client_secret_key (str): Keycloak client secret key
43
- virtual_host (str): RabbitMQ virtual host
44
- is_tls (bool): True, if the connection uses Transport Layer Security (TLS)
45
- yaml_file (str): Path to the YAML configuration file
46
- """
47
-
48
- def __init__(
49
- self,
50
- username: str = None,
51
- password: str = None,
52
- rabbitmq_host: str = None,
53
- rabbitmq_port: int = None,
54
- keycloak_host: str = None,
55
- keycloak_port: int = None,
56
- keycloak_realm: str = None,
57
- client_id: str = None,
58
- client_secret_key: str = None,
59
- virtual_host: str = None,
60
- is_tls: bool = True,
61
- yaml_file: str = None,
62
- ):
63
- """
64
- Initializes a new connection configuration.
65
-
66
- Args:
67
- username (str): client username, provided by NOS-T operator
68
- password (str): client password, provided by NOS-T operator
69
- host (str): broker hostname
70
- rabbitmq_port (int): RabbitMQ broker port number
71
- keycloak_port (int): Keycloak IAM port number
72
- keycloak_realm (str): Keycloak realm name
73
- client_id (str): Keycloak client ID
74
- client_secret_key (str): Keycloak client secret key
75
- virtual_host (str): RabbitMQ virtual host
76
- is_tls (bool): True, if the connection uses TLS
77
- yaml_file (str): Path to the YAML configuration file
78
- """
79
- self.username = username
80
- self.password = password
81
- self.rabbitmq_host = rabbitmq_host
82
- self.keycloak_host = keycloak_host
83
- self.rabbitmq_port = rabbitmq_port
84
- self.keycloak_port = keycloak_port
85
- self.keycloak_realm = keycloak_realm
86
- self.client_id = client_id
87
- self.client_secret_key = client_secret_key
88
- self.virtual_host = virtual_host
89
- self.is_tls = is_tls
90
-
91
- self.yaml_config = None
92
- self.predefined_exchanges_queues = False
93
- self.yaml_file = yaml_file
94
- self.unique_exchanges = {}
95
- self.channel_configs = []
96
-
97
- self.create_connection_config()
98
-
99
- def get_exchanges(self):
100
- """
101
- Get exchanges from the YAML configuration file.
102
- """
103
- for app, app_channels in self.yaml_config.channels.items():
104
- for channel, details in app_channels.items():
105
- bindings = details.get("bindings", {}).get("amqp", {})
106
- exchange = bindings.get("exchange", {})
107
- exchange_name = exchange.get("name")
108
- if exchange_name:
109
- exchange_config = ExchangeConfig(
110
- name=exchange_name,
111
- type=exchange.get("type", "topic"),
112
- durable=exchange.get("durable", True),
113
- auto_delete=exchange.get("autoDelete", False),
114
- vhost=exchange.get("vhost", "/"),
115
- )
116
- if exchange_name in self.unique_exchanges:
117
- if (
118
- self.unique_exchanges[exchange_name]
119
- != exchange_config.model_dump()
120
- ):
121
- raise ValueError(
122
- f"Conflicting configurations for exchange '{exchange_name}': {self.unique_exchanges[exchange_name]} vs {exchange_config.model_dump()}"
123
- )
124
- else:
125
- self.unique_exchanges[exchange_name] = (
126
- exchange_config.model_dump()
127
- )
128
-
129
- def get_channels(self):
130
- """
131
- Get channels from the YAML configuration file.
132
- """
133
- for app, app_channels in self.yaml_config.channels.items():
134
- for channel, details in app_channels.items():
135
- bindings = details.get("bindings", {}).get("amqp", {})
136
- exchange = bindings.get("exchange", {})
137
- exchange_name = exchange.get("name")
138
- address = details.get("address")
139
- if address and bindings:
140
- channel_config = ChannelConfig(
141
- app=app,
142
- address=address,
143
- exchange=exchange_name or "default_exchange",
144
- durable=exchange.get("durable", True),
145
- auto_delete=exchange.get("autoDelete", False),
146
- vhost=exchange.get("vhost", "/"),
147
- )
148
- self.channel_configs.append(channel_config.model_dump())
149
-
150
- def get_exchanges_channels(self):
151
- """
152
- Get exchanges and channels from the YAML configuration file.
153
- """
154
-
155
- self.get_exchanges(), self.get_channels()
156
- if self.unique_exchanges and self.channel_configs:
157
- self.predefined_exchanges_queues = True
158
- self.simulation_config = SimulationConfig(
159
- exchanges=self.unique_exchanges,
160
- queues=self.channel_configs,
161
- execution_parameters=self.yaml_config.execution,
162
- predefined_exchanges_queues=self.predefined_exchanges_queues,
163
- )
164
-
165
- def load_environment_variables(self):
166
- """
167
- Loads an environment (.env) file and returns the parsed data.
168
- """
169
- dotenv_path = find_dotenv(usecwd=True)
170
- if dotenv_path:
171
- logger.info(f"Checking for credentials in the .env file: {dotenv_path}.")
172
- load_dotenv(dotenv_path, override=True)
173
- else:
174
- logger.warning(
175
- "Checking for credentials in the system environment variables."
176
- )
177
- if self.server_config.servers.rabbitmq.keycloak_authentication:
178
- required_fields = [
179
- "USERNAME",
180
- "PASSWORD",
181
- "CLIENT_ID",
182
- "CLIENT_SECRET_KEY",
183
- ]
184
- else:
185
- required_fields = ["USERNAME", "PASSWORD"]
186
-
187
- env_data = {field: os.getenv(field) for field in required_fields}
188
-
189
- missing_fields = [field for field, value in env_data.items() if value is None]
190
- if missing_fields:
191
- raise EnvironmentVariableError(
192
- f"Missing required fields in .env file: {', '.join(missing_fields)}"
193
- )
194
-
195
- try:
196
- if self.server_config.servers.rabbitmq.keycloak_authentication:
197
- self.credentials_config = Credentials(
198
- username=env_data["USERNAME"],
199
- password=env_data["PASSWORD"],
200
- client_id=env_data["CLIENT_ID"],
201
- client_secret_key=env_data["CLIENT_SECRET_KEY"],
202
- )
203
- else:
204
- self.credentials_config = Credentials(
205
- username=env_data["USERNAME"],
206
- password=env_data["PASSWORD"],
207
- )
208
- except ValidationError as err:
209
- raise EnvironmentVariableError(f"Invalid environment variables: {err}")
210
-
211
- def load_yaml_config_file(self):
212
- """
213
- Loads a YAML configuration file and returns the parsed data.
214
- """
215
- if not os.path.exists(self.yaml_file):
216
- raise ConfigurationError("Couldn't load config file (not found)")
217
-
218
- with open(self.yaml_file, "r", encoding="utf-8") as f:
219
- try:
220
- yaml_data = yaml.safe_load(f) # Store the parsed YAML data
221
- except yaml.YAMLError as err:
222
- raise ConfigurationError(f"Invalid YAML configuration: {err}")
223
-
224
- try:
225
- self.yaml_config = Config(**yaml_data)
226
- except ValidationError as err:
227
- raise ConfigurationError(f"Invalid configuration: {err}")
228
-
229
- def create_connection_config(self):
230
- """
231
- Creates a connection configuration.
232
- """
233
- if self.yaml_file:
234
- try:
235
- self.load_yaml_config_file()
236
- except ConfigurationError as e:
237
- raise ValueError(f"Configuration error: {e}")
238
-
239
- try:
240
- assert all(
241
- item in self.yaml_config.execution.required_apps
242
- for item in self.yaml_config.channels.keys()
243
- ), "Application names do not match the channels defined in the configuration file."
244
- except ConfigAssertionError as e:
245
- raise ValueError(f"Assertion error: {e}")
246
- else:
247
- try:
248
- self.yaml_config = Config(
249
- servers=ServersConfig(
250
- rabbitmq=RabbitMQConfig(
251
- host=self.rabbitmq_host,
252
- port=self.rabbitmq_port,
253
- virtual_host=self.virtual_host,
254
- tls=self.is_tls,
255
- ),
256
- keycloak=KeycloakConfig(
257
- host=self.keycloak_host,
258
- port=self.keycloak_port,
259
- realm=self.keycloak_realm,
260
- tls=self.is_tls,
261
- ),
262
- ),
263
- channels={},
264
- execution=ExecConfig(),
265
- )
266
- except:
267
- self.yaml_config = Config(
268
- servers=ServersConfig(
269
- rabbitmq=RabbitMQConfig(), keycloak=KeycloakConfig()
270
- ),
271
- channels={},
272
- execution=ExecConfig(),
273
- )
274
-
275
- self.get_exchanges_channels()
276
-
277
- server_config = self.yaml_config.copy()
278
- if hasattr(server_config, "channels"):
279
- del server_config.channels
280
- if hasattr(server_config, "execution"):
281
- del server_config.execution
282
- self.server_config = server_config
283
-
284
- if (
285
- self.username is not None
286
- and self.password is not None
287
- and self.client_id is not None
288
- and self.client_secret_key is not None
289
- ):
290
- logger.info("Using provided credentials.")
291
- self.credentials_config = Credentials(
292
- username=self.username,
293
- password=self.password,
294
- client_id=self.client_id,
295
- client_secret_key=self.client_secret_key,
296
- )
297
- else:
298
- self.load_environment_variables()
299
-
300
- self.rc = RuntimeConfig(
301
- credentials=self.credentials_config,
302
- server_configuration=server_config,
303
- simulation_configuration=self.simulation_config,
304
- )
1
+ """
2
+ Configuration Settings.
3
+ """
4
+
5
+ import logging
6
+ import os
7
+
8
+ import yaml
9
+ from dotenv import find_dotenv, load_dotenv
10
+ from pydantic import ValidationError
11
+
12
+ from .errors import ConfigAssertionError, ConfigurationError, EnvironmentVariableError
13
+ from .schemas import (
14
+ ChannelConfig,
15
+ Config,
16
+ Credentials,
17
+ ExchangeConfig,
18
+ ExecConfig,
19
+ KeycloakConfig,
20
+ RabbitMQConfig,
21
+ RuntimeConfig,
22
+ ServersConfig,
23
+ SimulationConfig,
24
+ )
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ class ConnectionConfig:
29
+ """Connection configuration.
30
+
31
+ The configuration settings to establish a connection to the broker, including authentication for the
32
+ user and identification of the server.
33
+
34
+ Attributes:
35
+ username (str): client username, provided by NOS-T operator
36
+ password (str): client password, provided by NOS-T operator
37
+ host (str): broker hostname
38
+ rabbitmq_port (int): RabbitMQ broker port number
39
+ keycloak_port (int): Keycloak IAM port number
40
+ keycloak_realm (str): Keycloak realm name
41
+ client_id (str): Keycloak client ID
42
+ client_secret_key (str): Keycloak client secret key
43
+ virtual_host (str): RabbitMQ virtual host
44
+ is_tls (bool): True, if the connection uses Transport Layer Security (TLS)
45
+ yaml_file (str): Path to the YAML configuration file
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ username: str = None,
51
+ password: str = None,
52
+ rabbitmq_host: str = None,
53
+ rabbitmq_port: int = None,
54
+ keycloak_host: str = None,
55
+ keycloak_port: int = None,
56
+ keycloak_realm: str = None,
57
+ client_id: str = None,
58
+ client_secret_key: str = None,
59
+ virtual_host: str = None,
60
+ is_tls: bool = True,
61
+ yaml_file: str = None,
62
+ ):
63
+ """
64
+ Initializes a new connection configuration.
65
+
66
+ Args:
67
+ username (str): client username, provided by NOS-T operator
68
+ password (str): client password, provided by NOS-T operator
69
+ host (str): broker hostname
70
+ rabbitmq_port (int): RabbitMQ broker port number
71
+ keycloak_port (int): Keycloak IAM port number
72
+ keycloak_realm (str): Keycloak realm name
73
+ client_id (str): Keycloak client ID
74
+ client_secret_key (str): Keycloak client secret key
75
+ virtual_host (str): RabbitMQ virtual host
76
+ is_tls (bool): True, if the connection uses TLS
77
+ yaml_file (str): Path to the YAML configuration file
78
+ """
79
+ self.username = username
80
+ self.password = password
81
+ self.rabbitmq_host = rabbitmq_host
82
+ self.keycloak_host = keycloak_host
83
+ self.rabbitmq_port = rabbitmq_port
84
+ self.keycloak_port = keycloak_port
85
+ self.keycloak_realm = keycloak_realm
86
+ self.client_id = client_id
87
+ self.client_secret_key = client_secret_key
88
+ self.virtual_host = virtual_host
89
+ self.is_tls = is_tls
90
+
91
+ self.yaml_config = None
92
+ self.predefined_exchanges_queues = False
93
+ self.yaml_file = yaml_file
94
+ self.unique_exchanges = {}
95
+ self.channel_configs = []
96
+
97
+ self.create_connection_config()
98
+
99
+ def get_exchanges(self):
100
+ """
101
+ Get exchanges from the YAML configuration file.
102
+ """
103
+ for app, app_channels in self.yaml_config.channels.items():
104
+ for channel, details in app_channels.items():
105
+ bindings = details.get("bindings", {}).get("amqp", {})
106
+ exchange = bindings.get("exchange", {})
107
+ exchange_name = exchange.get("name")
108
+ if exchange_name:
109
+ exchange_config = ExchangeConfig(
110
+ name=exchange_name,
111
+ type=exchange.get("type", "topic"),
112
+ durable=exchange.get("durable", True),
113
+ auto_delete=exchange.get("autoDelete", False),
114
+ vhost=exchange.get("vhost", "/"),
115
+ )
116
+ if exchange_name in self.unique_exchanges:
117
+ if (
118
+ self.unique_exchanges[exchange_name]
119
+ != exchange_config.model_dump()
120
+ ):
121
+ raise ValueError(
122
+ f"Conflicting configurations for exchange '{exchange_name}': {self.unique_exchanges[exchange_name]} vs {exchange_config.model_dump()}"
123
+ )
124
+ else:
125
+ self.unique_exchanges[exchange_name] = (
126
+ exchange_config.model_dump()
127
+ )
128
+
129
+ def get_channels(self):
130
+ """
131
+ Get channels from the YAML configuration file.
132
+ """
133
+ for app, app_channels in self.yaml_config.channels.items():
134
+ for channel, details in app_channels.items():
135
+ bindings = details.get("bindings", {}).get("amqp", {})
136
+ exchange = bindings.get("exchange", {})
137
+ exchange_name = exchange.get("name")
138
+ address = details.get("address")
139
+ if address and bindings:
140
+ channel_config = ChannelConfig(
141
+ app=app,
142
+ address=address,
143
+ exchange=exchange_name or "default_exchange",
144
+ durable=exchange.get("durable", True),
145
+ auto_delete=exchange.get("autoDelete", False),
146
+ vhost=exchange.get("vhost", "/"),
147
+ )
148
+ self.channel_configs.append(channel_config.model_dump())
149
+
150
+ def get_exchanges_channels(self):
151
+ """
152
+ Get exchanges and channels from the YAML configuration file.
153
+ """
154
+
155
+ self.get_exchanges(), self.get_channels()
156
+ if self.unique_exchanges and self.channel_configs:
157
+ self.predefined_exchanges_queues = True
158
+ self.simulation_config = SimulationConfig(
159
+ exchanges=self.unique_exchanges,
160
+ queues=self.channel_configs,
161
+ execution_parameters=self.yaml_config.execution,
162
+ predefined_exchanges_queues=self.predefined_exchanges_queues,
163
+ )
164
+
165
+ def load_environment_variables(self):
166
+ """
167
+ Loads an environment (.env) file and returns the parsed data.
168
+ """
169
+ dotenv_path = find_dotenv(usecwd=True)
170
+ if dotenv_path:
171
+ logger.info(f"Checking for credentials in the .env file: {dotenv_path}.")
172
+ load_dotenv(dotenv_path, override=True)
173
+ else:
174
+ logger.warning(
175
+ "Checking for credentials in the system environment variables."
176
+ )
177
+ if self.server_config.servers.rabbitmq.keycloak_authentication:
178
+ required_fields = [
179
+ "USERNAME",
180
+ "PASSWORD",
181
+ "CLIENT_ID",
182
+ "CLIENT_SECRET_KEY",
183
+ ]
184
+ else:
185
+ required_fields = ["USERNAME", "PASSWORD"]
186
+
187
+ env_data = {field: os.getenv(field) for field in required_fields}
188
+
189
+ missing_fields = [field for field, value in env_data.items() if value is None]
190
+ if missing_fields:
191
+ raise EnvironmentVariableError(
192
+ f"Missing required fields in .env file: {', '.join(missing_fields)}"
193
+ )
194
+
195
+ try:
196
+ if self.server_config.servers.rabbitmq.keycloak_authentication:
197
+ self.credentials_config = Credentials(
198
+ username=env_data["USERNAME"],
199
+ password=env_data["PASSWORD"],
200
+ client_id=env_data["CLIENT_ID"],
201
+ client_secret_key=env_data["CLIENT_SECRET_KEY"],
202
+ )
203
+ else:
204
+ self.credentials_config = Credentials(
205
+ username=env_data["USERNAME"],
206
+ password=env_data["PASSWORD"],
207
+ )
208
+ except ValidationError as err:
209
+ raise EnvironmentVariableError(f"Invalid environment variables: {err}")
210
+
211
+ def load_yaml_config_file(self):
212
+ """
213
+ Loads a YAML configuration file and returns the parsed data.
214
+ """
215
+ if not os.path.exists(self.yaml_file):
216
+ raise ConfigurationError("Couldn't load config file (not found)")
217
+
218
+ with open(self.yaml_file, "r", encoding="utf-8") as f:
219
+ try:
220
+ yaml_data = yaml.safe_load(f) # Store the parsed YAML data
221
+ except yaml.YAMLError as err:
222
+ raise ConfigurationError(f"Invalid YAML configuration: {err}")
223
+
224
+ try:
225
+ self.yaml_config = Config(**yaml_data)
226
+ except ValidationError as err:
227
+ raise ConfigurationError(f"Invalid configuration: {err}")
228
+
229
+ def create_connection_config(self):
230
+ """
231
+ Creates a connection configuration.
232
+ """
233
+ if self.yaml_file:
234
+ try:
235
+ self.load_yaml_config_file()
236
+ except ConfigurationError as e:
237
+ raise ValueError(f"Configuration error: {e}")
238
+
239
+ try:
240
+ assert all(
241
+ item in self.yaml_config.execution.required_apps
242
+ for item in self.yaml_config.channels.keys()
243
+ ), "Application names do not match the channels defined in the configuration file."
244
+ except ConfigAssertionError as e:
245
+ raise ValueError(f"Assertion error: {e}")
246
+ else:
247
+ try:
248
+ self.yaml_config = Config(
249
+ servers=ServersConfig(
250
+ rabbitmq=RabbitMQConfig(
251
+ host=self.rabbitmq_host,
252
+ port=self.rabbitmq_port,
253
+ virtual_host=self.virtual_host,
254
+ tls=self.is_tls,
255
+ ),
256
+ keycloak=KeycloakConfig(
257
+ host=self.keycloak_host,
258
+ port=self.keycloak_port,
259
+ realm=self.keycloak_realm,
260
+ tls=self.is_tls,
261
+ ),
262
+ ),
263
+ channels={},
264
+ execution=ExecConfig(),
265
+ )
266
+ except:
267
+ self.yaml_config = Config(
268
+ servers=ServersConfig(
269
+ rabbitmq=RabbitMQConfig(), keycloak=KeycloakConfig()
270
+ ),
271
+ channels={},
272
+ execution=ExecConfig(),
273
+ )
274
+
275
+ self.get_exchanges_channels()
276
+
277
+ server_config = self.yaml_config.copy()
278
+ if hasattr(server_config, "channels"):
279
+ del server_config.channels
280
+ if hasattr(server_config, "execution"):
281
+ del server_config.execution
282
+ self.server_config = server_config
283
+
284
+ if (
285
+ self.username is not None
286
+ and self.password is not None
287
+ and self.client_id is not None
288
+ and self.client_secret_key is not None
289
+ ):
290
+ logger.info("Using provided credentials.")
291
+ self.credentials_config = Credentials(
292
+ username=self.username,
293
+ password=self.password,
294
+ client_id=self.client_id,
295
+ client_secret_key=self.client_secret_key,
296
+ )
297
+ else:
298
+ self.load_environment_variables()
299
+
300
+ self.rc = RuntimeConfig(
301
+ credentials=self.credentials_config,
302
+ server_configuration=server_config,
303
+ simulation_configuration=self.simulation_config,
304
+ )