osism 0.20250627.0__py3-none-any.whl → 0.20250628.0__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.
osism/api.py CHANGED
@@ -3,10 +3,11 @@
3
3
  import datetime
4
4
  from logging.config import dictConfig
5
5
  import logging
6
+ from typing import Optional, Dict, Any
6
7
  from uuid import UUID
7
8
 
8
- from fastapi import FastAPI, Header, Request, Response
9
- from pydantic import BaseModel
9
+ from fastapi import FastAPI, Header, Request, Response, HTTPException, status
10
+ from pydantic import BaseModel, Field
10
11
  from starlette.middleware.cors import CORSMiddleware
11
12
 
12
13
  from osism.tasks import reconciler
@@ -15,186 +16,311 @@ from osism.services.listener import BaremetalEvents
15
16
 
16
17
 
17
18
  class NotificationBaremetal(BaseModel):
18
- priority: str
19
- event_type: str
20
- timestamp: str
21
- publisher_id: str
22
- message_id: UUID
23
- payload: dict
19
+ priority: str = Field(..., description="Notification priority level")
20
+ event_type: str = Field(..., description="Type of the event")
21
+ timestamp: str = Field(..., description="Event timestamp")
22
+ publisher_id: str = Field(..., description="ID of the event publisher")
23
+ message_id: UUID = Field(..., description="Unique message identifier")
24
+ payload: Dict[str, Any] = Field(..., description="Event payload data")
24
25
 
25
26
 
26
27
  class WebhookNetboxResponse(BaseModel):
27
- result: str
28
+ result: str = Field(..., description="Operation result status")
28
29
 
29
30
 
30
31
  class WebhookNetboxData(BaseModel):
31
- username: str
32
- data: dict
33
- snapshots: dict
34
- event: str
35
- timestamp: datetime.datetime
36
- model: str
37
- request_id: UUID
32
+ username: str = Field(..., description="Username triggering the webhook")
33
+ data: Dict[str, Any] = Field(..., description="Webhook data payload")
34
+ snapshots: Dict[str, Any] = Field(..., description="Data snapshots")
35
+ event: str = Field(..., description="Event type")
36
+ timestamp: datetime.datetime = Field(..., description="Event timestamp")
37
+ model: str = Field(..., description="Model type")
38
+ request_id: UUID = Field(..., description="Unique request identifier")
38
39
 
39
40
 
40
- # https://stackoverflow.com/questions/63510041/adding-python-logging-to-fastapi-endpoints-hosted-on-docker-doesnt-display-api
41
41
  class LogConfig(BaseModel):
42
- """Logging configuration to be set for the server"""
42
+ """Logging configuration for the OSISM API server."""
43
43
 
44
44
  LOGGER_NAME: str = "osism"
45
- LOG_FORMAT: str = "%(levelprefix)s | %(asctime)s | %(message)s"
46
- LOG_LEVEL: str = "DEBUG"
45
+ LOG_FORMAT: str = "%(levelname)s | %(asctime)s | %(name)s | %(message)s"
46
+ LOG_LEVEL: str = "INFO"
47
47
 
48
48
  # Logging config
49
49
  version: int = 1
50
50
  disable_existing_loggers: bool = False
51
- formatters: dict = {
51
+ formatters: Dict[str, Any] = {
52
52
  "default": {
53
- "()": "uvicorn.logging.DefaultFormatter",
54
- "fmt": LOG_FORMAT,
53
+ "format": LOG_FORMAT,
55
54
  "datefmt": "%Y-%m-%d %H:%M:%S",
56
55
  },
57
56
  }
58
- handlers: dict = {
57
+ handlers: Dict[str, Any] = {
59
58
  "default": {
60
59
  "formatter": "default",
61
60
  "class": "logging.StreamHandler",
62
61
  "stream": "ext://sys.stderr",
63
62
  },
64
63
  }
65
- loggers: dict = {
66
- "api": {"handlers": ["default"], "level": LOG_LEVEL},
64
+ loggers: Dict[str, Any] = {
65
+ "osism": {"handlers": ["default"], "level": LOG_LEVEL, "propagate": False},
66
+ "api": {"handlers": ["default"], "level": LOG_LEVEL, "propagate": False},
67
+ "uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
68
+ "uvicorn.error": {"handlers": ["default"], "level": "INFO", "propagate": False},
69
+ "uvicorn.access": {
70
+ "handlers": ["default"],
71
+ "level": "INFO",
72
+ "propagate": False,
73
+ },
67
74
  }
68
75
 
69
76
 
70
- app = FastAPI()
71
-
72
- app.add_middleware(CORSMiddleware)
73
-
74
- dictConfig(LogConfig().dict())
75
- logger = logging.getLogger("api")
77
+ app = FastAPI(
78
+ title="OSISM API",
79
+ description="API for OpenStack Infrastructure & Service Manager",
80
+ version="1.0.0",
81
+ docs_url="/docs",
82
+ redoc_url="/redoc",
83
+ )
84
+
85
+ # Configure CORS - in production, replace with specific origins
86
+ app.add_middleware(
87
+ CORSMiddleware,
88
+ allow_origins=["*"], # TODO: Replace with actual allowed origins in production
89
+ allow_credentials=True,
90
+ allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
91
+ allow_headers=[
92
+ "Accept",
93
+ "Accept-Language",
94
+ "Content-Language",
95
+ "Content-Type",
96
+ "Authorization",
97
+ "X-Hook-Signature",
98
+ ],
99
+ )
100
+
101
+ dictConfig(LogConfig().model_dump())
102
+ logger = logging.getLogger("osism.api")
76
103
 
77
104
  baremetal_events = BaremetalEvents()
78
105
 
79
106
 
80
- @app.get("/")
81
- async def root():
82
- return {"result": "ok"}
83
-
84
-
85
- @app.get("/v1")
86
- async def v1():
87
- return {"result": "ok"}
88
-
89
-
90
- @app.post("/v1/meters/sink")
91
- async def write_sink_meters(request: Request):
92
- data = await request.json()
93
-
94
-
95
- @app.post("/v1/events/sink")
96
- async def write_sink_events(request: Request):
97
- data = await request.json()
98
-
99
-
100
- @app.post("/v1/notifications/baremetal", status_code=204)
101
- async def notifications_baremetal(notification: NotificationBaremetal) -> None:
102
-
103
- handler = baremetal_events.get_handler(notification.event_type)
104
- handler(notification.payload)
107
+ class DeviceSearchResult(BaseModel):
108
+ result: str = Field(..., description="Operation result status")
109
+ device: Optional[str] = Field(None, description="Device name if found")
105
110
 
106
111
 
107
- @app.post("/v1/switches/{identifier}/ztp/complete")
108
- async def switches_ztp_complete(identifier: str):
112
+ def find_device_by_identifier(identifier: str):
113
+ """Find a device in NetBox by various identifiers."""
109
114
  if not utils.nb:
110
- return {"result": "netbox not enabled"}
115
+ return None
111
116
 
112
117
  device = None
113
118
 
114
119
  # Search by device name
115
120
  devices = utils.nb.dcim.devices.filter(name=identifier)
116
121
  if devices:
117
- device = devices[0]
122
+ device = list(devices)[0]
118
123
 
119
124
  # Search by inventory_hostname custom field
120
125
  if not device:
121
126
  devices = utils.nb.dcim.devices.filter(cf_inventory_hostname=identifier)
122
127
  if devices:
123
- device = devices[0]
128
+ device = list(devices)[0]
124
129
 
125
130
  # Search by serial number
126
131
  if not device:
127
132
  devices = utils.nb.dcim.devices.filter(serial=identifier)
128
133
  if devices:
129
- device = devices[0]
134
+ device = list(devices)[0]
130
135
 
131
- if device:
136
+ return device
137
+
138
+
139
+ @app.get("/", tags=["health"])
140
+ async def root() -> Dict[str, str]:
141
+ """Health check endpoint."""
142
+ return {"result": "ok"}
143
+
144
+
145
+ @app.get("/v1", tags=["health"])
146
+ async def v1() -> Dict[str, str]:
147
+ """API version 1 health check endpoint."""
148
+ return {"result": "ok"}
149
+
150
+
151
+ class SinkResponse(BaseModel):
152
+ result: str = Field(..., description="Operation result status")
153
+
154
+
155
+ @app.post("/v1/meters/sink", response_model=SinkResponse, tags=["telemetry"])
156
+ async def write_sink_meters(request: Request) -> SinkResponse:
157
+ """Write telemetry meters to sink."""
158
+ try:
159
+ data = await request.json()
160
+ # TODO: Implement meter processing logic
132
161
  logger.info(
133
- f"Found device {device.name} for ZTP complete with identifier {identifier}"
162
+ f"Received meters data: {len(data) if isinstance(data, list) else 1} entries"
163
+ )
164
+ return SinkResponse(result="ok")
165
+ except Exception as e:
166
+ logger.error(f"Error processing meters: {str(e)}")
167
+ raise HTTPException(
168
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
169
+ detail="Failed to process meters data",
134
170
  )
135
171
 
136
- # Set provision_state custom field to active
137
- device.custom_fields["provision_state"] = "active"
138
- device.save()
139
172
 
140
- return {"result": "ok", "device": device.name}
173
+ @app.post("/v1/events/sink", response_model=SinkResponse, tags=["telemetry"])
174
+ async def write_sink_events(request: Request) -> SinkResponse:
175
+ """Write telemetry events to sink."""
176
+ try:
177
+ data = await request.json()
178
+ # TODO: Implement event processing logic
179
+ logger.info(
180
+ f"Received events data: {len(data) if isinstance(data, list) else 1} entries"
181
+ )
182
+ return SinkResponse(result="ok")
183
+ except Exception as e:
184
+ logger.error(f"Error processing events: {str(e)}")
185
+ raise HTTPException(
186
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
187
+ detail="Failed to process events data",
188
+ )
189
+
190
+
191
+ @app.post("/v1/notifications/baremetal", status_code=204, tags=["notifications"])
192
+ async def notifications_baremetal(notification: NotificationBaremetal) -> None:
193
+ """Handle baremetal notifications."""
194
+ try:
195
+ handler = baremetal_events.get_handler(notification.event_type)
196
+ handler(notification.payload)
197
+ logger.info(
198
+ f"Successfully processed baremetal notification: {notification.event_type}"
199
+ )
200
+ except Exception as e:
201
+ logger.error(f"Error processing baremetal notification: {str(e)}")
202
+ if isinstance(e, HTTPException):
203
+ raise
204
+ raise HTTPException(
205
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
206
+ detail="Failed to process baremetal notification",
207
+ )
208
+
209
+
210
+ @app.post(
211
+ "/v1/switches/{identifier}/ztp/complete",
212
+ response_model=DeviceSearchResult,
213
+ tags=["switches"],
214
+ )
215
+ async def switches_ztp_complete(identifier: str) -> DeviceSearchResult:
216
+ """Mark a switch as ZTP complete by setting provision_state to active."""
217
+ if not utils.nb:
218
+ raise HTTPException(
219
+ status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
220
+ detail="NetBox is not enabled",
221
+ )
222
+
223
+ try:
224
+ device = find_device_by_identifier(identifier)
225
+
226
+ if device:
227
+ logger.info(
228
+ f"Found device {device.name} for ZTP complete with identifier {identifier}"
229
+ )
230
+
231
+ # Set provision_state custom field to active
232
+ device.custom_fields["provision_state"] = "active"
233
+ device.save()
234
+
235
+ return DeviceSearchResult(result="ok", device=device.name)
236
+ else:
237
+ logger.warning(
238
+ f"No device found for ZTP complete with identifier {identifier}"
239
+ )
240
+ raise HTTPException(
241
+ status_code=status.HTTP_404_NOT_FOUND,
242
+ detail=f"Device not found with identifier: {identifier}",
243
+ )
244
+ except Exception as e:
245
+ logger.error(f"Error completing ZTP for device {identifier}: {str(e)}")
246
+ if isinstance(e, HTTPException):
247
+ raise
248
+ raise HTTPException(
249
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
250
+ detail="Failed to complete ZTP process",
251
+ )
252
+
253
+
254
+ def process_netbox_webhook(webhook_input: WebhookNetboxData) -> None:
255
+ """Process NetBox webhook data."""
256
+ data = webhook_input.data
257
+ url = data["url"]
258
+ name = data["name"]
259
+
260
+ if "devices" in url:
261
+ tags = [x["name"] for x in data["tags"]]
262
+ custom_fields = data["custom_fields"]
263
+ device_type = custom_fields.get("device_type") or "node"
264
+
265
+ elif "interfaces" in url:
266
+ device_type = "interface"
267
+ device_id = data["device"]["id"]
268
+ device = utils.nb.dcim.devices.get(id=device_id)
269
+ tags = [str(x) for x in device.tags]
270
+ custom_fields = device.custom_fields
271
+ else:
272
+ logger.warning(f"Unknown webhook URL type: {url}")
273
+ return
274
+
275
+ if "Managed by OSISM" in tags:
276
+ if device_type == "server":
277
+ logger.info(
278
+ f"Handling change for managed device {name} of type {device_type}"
279
+ )
280
+ reconciler.run.delay()
281
+ elif device_type == "switch":
282
+ logger.info(
283
+ f"Handling change for managed device {name} of type {device_type}"
284
+ )
285
+ # TODO: Implement switch configuration generation
286
+ # netbox.generate.delay(name, custom_fields['configuration_template'])
287
+ elif device_type == "interface":
288
+ logger.info(
289
+ f"Handling change for interface {name} on managed device {device.name} of type {custom_fields['device_type']}"
290
+ )
291
+ # TODO: Implement interface configuration generation
292
+ # netbox.generate.delay(device.name, custom_fields['configuration_template'])
141
293
  else:
142
- logger.warning(f"No device found for ZTP complete with identifier {identifier}")
143
- return {"result": "device not found"}
294
+ logger.info(f"Ignoring change for unmanaged device {name}")
144
295
 
145
296
 
146
- @app.post("/v1/webhook/netbox", response_model=WebhookNetboxResponse, status_code=200)
297
+ @app.post(
298
+ "/v1/webhook/netbox",
299
+ response_model=WebhookNetboxResponse,
300
+ status_code=200,
301
+ tags=["webhooks"],
302
+ )
147
303
  async def webhook(
148
304
  webhook_input: WebhookNetboxData,
149
305
  request: Request,
150
306
  response: Response,
151
307
  content_length: int = Header(...),
152
- x_hook_signature: str = Header(None),
153
- ):
154
- if utils.nb:
155
- data = webhook_input.data
156
- url = data["url"]
157
- name = data["name"]
158
-
159
- if "devices" in url:
160
- tags = [x["name"] for x in data["tags"]]
161
-
162
- custom_fields = data["custom_fields"]
163
- device_type = custom_fields["device_type"]
164
-
165
- # NOTE: device without a defined device_type are nodes
166
- if not device_type:
167
- device_type = "node"
168
-
169
- elif "interfaces" in url:
170
- device_type = "interface"
171
-
172
- device_id = data["device"]["id"]
173
- device = utils.nb.dcim.devices.get(id=device_id)
174
- tags = [str(x) for x in device.tags]
175
- custom_fields = device.custom_fields
176
-
177
- if "Managed by OSISM" in tags:
178
- if device_type == "server":
179
- logger.info(
180
- f"Handling change for managed device {name} of type {device_type}"
181
- )
182
- reconciler.run.delay()
183
- elif device_type == "switch":
184
- logger.info(
185
- f"Handling change for managed device {name} of type {device_type}"
186
- )
187
- # netbox.generate.delay(name, custom_fields['configuration_template'])
188
- elif device_type == "interface":
189
- logger.info(
190
- f"Handling change for interface {name} on managed device {device.name} of type {custom_fields['device_type']}"
191
- )
192
- # netbox.generate.delay(device.name, custom_fields['configuration_template'])
193
-
194
- else:
195
- logger.info(f"Ignoring change for unmanaged device {name}")
196
-
197
- return {"result": "ok"}
308
+ x_hook_signature: Optional[str] = Header(None),
309
+ ) -> WebhookNetboxResponse:
310
+ """Handle NetBox webhook notifications."""
311
+ if not utils.nb:
312
+ raise HTTPException(
313
+ status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
314
+ detail="NetBox webhook processing is not enabled",
315
+ )
198
316
 
199
- else:
200
- return {"result": "webhook netbox not enabled"}
317
+ try:
318
+ # TODO: Validate webhook signature if x_hook_signature is provided
319
+ process_netbox_webhook(webhook_input)
320
+ return WebhookNetboxResponse(result="ok")
321
+ except Exception as e:
322
+ logger.error(f"Error processing NetBox webhook: {str(e)}")
323
+ raise HTTPException(
324
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
325
+ detail="Failed to process NetBox webhook",
326
+ )
osism/commands/apply.py CHANGED
@@ -9,8 +9,8 @@ from cliff.command import Command
9
9
  from loguru import logger
10
10
  from tabulate import tabulate
11
11
 
12
- from osism.core import enums
13
- from osism.core.playbooks import MAP_ROLE2ENVIRONMENT, MAP_ROLE2RUNTIME
12
+ from osism.data import enums
13
+ from osism.data.playbooks import MAP_ROLE2ENVIRONMENT, MAP_ROLE2RUNTIME
14
14
  from osism.tasks import ansible, ceph, kolla, kubernetes, handle_task
15
15
 
16
16
 
@@ -5,7 +5,7 @@ import argparse
5
5
  from cliff.command import Command
6
6
  from loguru import logger
7
7
 
8
- from osism.core.enums import VALIDATE_PLAYBOOKS
8
+ from osism.data.enums import VALIDATE_PLAYBOOKS
9
9
  from osism.tasks import ansible, ceph, kolla
10
10
  from osism import utils
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osism
3
- Version: 0.20250627.0
3
+ Version: 0.20250628.0
4
4
  Summary: OSISM manager interface
5
5
  Home-page: https://github.com/osism/python-osism
6
6
  Author: OSISM GmbH
@@ -53,7 +53,7 @@ Requires-Dist: sqlmodel==0.0.24
53
53
  Requires-Dist: sushy==5.6.0
54
54
  Requires-Dist: tabulate==0.9.0
55
55
  Requires-Dist: transitions==0.9.2
56
- Requires-Dist: uvicorn[standard]==0.34.3
56
+ Requires-Dist: uvicorn[standard]==0.35.0
57
57
  Requires-Dist: watchdog==6.0.0
58
58
  Provides-Extra: ansible
59
59
  Requires-Dist: ansible-runner==2.4.1; extra == "ansible"
@@ -1,11 +1,10 @@
1
1
  osism/__init__.py,sha256=1UiNTBus0V0f2AbZQzAtVtu6zkfCCrw0OTq--NwFAqY,341
2
2
  osism/__main__.py,sha256=ILe4gu61xEISiBsxanqTQIdSkV-YhpZXTRlguCYyssk,141
3
- osism/api.py,sha256=cvFLczibM6Hrc3KWDNN4daUauyLF7zpuU1jll-5ywPI,5585
3
+ osism/api.py,sha256=3kEfPJtPwuWD8luDNnEFRx3nEsUY5AeWnmjd4A5ii-A,11079
4
4
  osism/main.py,sha256=Dt2-9sLXcS-Ny4DAz7hrha-KRc7zd7BFUTRdfs_X8z4,893
5
5
  osism/settings.py,sha256=bebPBT6Hd1-KhJfwZdFR-s8eMwV4B1IFr-WrQBkOrWw,1786
6
- osism/actions/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
7
6
  osism/commands/__init__.py,sha256=Ag4wX_DCgXRdoLn6t069jqb3DdRylsX2nyYkiyCx4uk,456
8
- osism/commands/apply.py,sha256=q645f4qxmOAaUjVD7npzM2aNuOqfptVAkCLfE6x5IV8,16833
7
+ osism/commands/apply.py,sha256=GWUccZAXlgkPYqylrCmdWcj8FCkDsPEipIIG937MeII,16833
9
8
  osism/commands/baremetal.py,sha256=w1LyZDms-DtULsF_76JsauOYHc2tcvoMRP6F9dYby9E,10688
10
9
  osism/commands/compose.py,sha256=iqzG7mS9E1VWaLNN6yQowjOqiHn3BMdj-yfXb3Dc4Ok,1200
11
10
  osism/commands/compute.py,sha256=cgqXWJa5wAvn-7e3FWCgX6hie_aK0yrKRkcNzjLXwDY,25799
@@ -24,16 +23,14 @@ osism/commands/set.py,sha256=xLBi2DzbVQo2jb3-cOIE9In5UB3vFxquQJkDN-EsfhM,1425
24
23
  osism/commands/status.py,sha256=X-Rcj-XuNPDBoxsGkf96NswwpmTognxz1V6E2NX2ZgY,1997
25
24
  osism/commands/sync.py,sha256=jOg-g8NmVOkXBI6rOuiOx2WgUJc1PplLAAAwc0VuIfw,1919
26
25
  osism/commands/task.py,sha256=mwJJ7a71Lw3o_FX7j3rR0-NbPdPwMDOjbOAiiXE4uGc,543
27
- osism/commands/validate.py,sha256=E1n1kEo6h8J5c7Ns5OHpr0R7i4IU6uj08LE_gt3kBCg,3262
26
+ osism/commands/validate.py,sha256=ifdkelYptCXp18KX076Rb-CqluUN20bunG9ZeLs4AV8,3262
28
27
  osism/commands/vault.py,sha256=llaqNN8UH8t8cCu2KmdaURvprA4zeG6izCen_W7ulPs,2029
29
28
  osism/commands/volume.py,sha256=l6oAk__dFM8KKdLTWOvuSiI7tLh9wAPZp8hwmYF-NX0,6595
30
29
  osism/commands/wait.py,sha256=2Ncx63M0AFq4fq40VZVClf1LS-WHetD8iC_mG2dY_Cw,5275
31
30
  osism/commands/worker.py,sha256=iraCOEhCp7WgfjfZ0-12XQYQPUjpi9rSJK5Z9JfNJk4,1651
32
- osism/core/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
33
- osism/core/enums.py,sha256=gItIjOK6xWuOZSkMxpMdYLRyt4ezyhzkqA7BGiah2o0,10030
34
- osism/core/playbooks.py,sha256=M3T3ajV-8Lt-orsRO3jAoukhaoYFr4EZ2dzYXQjt1kg,728
35
31
  osism/data/__init__.py,sha256=izXdh0J3vPLQI7kBhJI7ibJQzPqU_nlONP0L4Cf_k6A,1504
36
- osism/plugins/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
32
+ osism/data/enums.py,sha256=gItIjOK6xWuOZSkMxpMdYLRyt4ezyhzkqA7BGiah2o0,10030
33
+ osism/data/playbooks.py,sha256=M3T3ajV-8Lt-orsRO3jAoukhaoYFr4EZ2dzYXQjt1kg,728
37
34
  osism/services/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
38
35
  osism/services/listener.py,sha256=Vf8LOZkHzlspm40BZ1az3o1O_ar34_i6C83p-D8KzzM,9783
39
36
  osism/tasks/__init__.py,sha256=kocG0q2bARhkkSjMBH2xWdFUIJodesdh5qVsV_DqZmE,7148
@@ -61,11 +58,11 @@ osism/tasks/conductor/sonic/exporter.py,sha256=25L1vbi84ZQD0xNHNTWk-anTz5QRkGJsk
61
58
  osism/tasks/conductor/sonic/interface.py,sha256=318wOwXYSSMKTPP2WSZIps-JvIkCQ2gYdQs9ZYHXwwg,38957
62
59
  osism/tasks/conductor/sonic/sync.py,sha256=fpgsQVwq6Hb7eeDHhLkAqx5BkaK3Ce_m_WvmWEsJyOo,9182
63
60
  osism/utils/__init__.py,sha256=gN5VtLJfrvyn6_snuTte7YR-vDygkpbORopIV8qSEsA,6064
64
- osism-0.20250627.0.dist-info/licenses/AUTHORS,sha256=oWotd63qsnNR945QLJP9mEXaXNtCMaesfo8ZNuLjwpU,39
65
- osism-0.20250627.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
66
- osism-0.20250627.0.dist-info/METADATA,sha256=z9qQh_i8ZIPgWTACdHuDa5mZKbmi9vWrTPQ5xUivpUI,2904
67
- osism-0.20250627.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
68
- osism-0.20250627.0.dist-info/entry_points.txt,sha256=eRq0DXdl4z2DdmPta6dqpzMe1M0DaUCNw9i4Jgdfhf0,3426
69
- osism-0.20250627.0.dist-info/pbr.json,sha256=rZRiHfI7rgnmVfh2COv00pQVKhMCCu6cw0bifLp9cVw,47
70
- osism-0.20250627.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
71
- osism-0.20250627.0.dist-info/RECORD,,
61
+ osism-0.20250628.0.dist-info/licenses/AUTHORS,sha256=oWotd63qsnNR945QLJP9mEXaXNtCMaesfo8ZNuLjwpU,39
62
+ osism-0.20250628.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
63
+ osism-0.20250628.0.dist-info/METADATA,sha256=rcVQq1RFwIJWgeRM4C6m33wkVLBrIWRr6uxb32HlP14,2904
64
+ osism-0.20250628.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
+ osism-0.20250628.0.dist-info/entry_points.txt,sha256=eRq0DXdl4z2DdmPta6dqpzMe1M0DaUCNw9i4Jgdfhf0,3426
66
+ osism-0.20250628.0.dist-info/pbr.json,sha256=NkG2tM3T6XeLebmfw4oRVNN2ALTn2QIvqsXacP_lLJU,47
67
+ osism-0.20250628.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
68
+ osism-0.20250628.0.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ {"git_version": "f3fe7d7", "is_release": false}
osism/actions/__init__.py DELETED
@@ -1 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
osism/core/__init__.py DELETED
@@ -1 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
osism/plugins/__init__.py DELETED
@@ -1 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
@@ -1 +0,0 @@
1
- {"git_version": "e1bd41d", "is_release": false}
File without changes
File without changes