osism 0.20250407.0__py3-none-any.whl → 0.20250505.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 +1 -16
- osism/commands/manage.py +3 -1
- osism/commands/server.py +116 -21
- osism/commands/volume.py +146 -49
- osism/settings.py +9 -0
- osism/tasks/__init__.py +7 -2
- osism/tasks/conductor.py +49 -62
- osism/tasks/netbox.py +67 -43
- osism/tasks/openstack.py +9 -42
- osism/utils/__init__.py +67 -12
- {osism-0.20250407.0.dist-info → osism-0.20250505.0.dist-info}/METADATA +10 -10
- {osism-0.20250407.0.dist-info → osism-0.20250505.0.dist-info}/RECORD +18 -18
- {osism-0.20250407.0.dist-info → osism-0.20250505.0.dist-info}/WHEEL +1 -1
- osism-0.20250505.0.dist-info/licenses/AUTHORS +1 -0
- osism-0.20250505.0.dist-info/pbr.json +1 -0
- osism-0.20250407.0.dist-info/licenses/AUTHORS +0 -1
- osism-0.20250407.0.dist-info/pbr.json +0 -1
- {osism-0.20250407.0.dist-info → osism-0.20250505.0.dist-info}/entry_points.txt +0 -0
- {osism-0.20250407.0.dist-info → osism-0.20250505.0.dist-info}/licenses/LICENSE +0 -0
- {osism-0.20250407.0.dist-info → osism-0.20250505.0.dist-info}/top_level.txt +0 -0
osism/api.py
CHANGED
@@ -7,11 +7,10 @@ from uuid import UUID
|
|
7
7
|
|
8
8
|
from fastapi import FastAPI, Header, Request, Response
|
9
9
|
from pydantic import BaseModel
|
10
|
-
import pynetbox
|
11
10
|
from starlette.middleware.cors import CORSMiddleware
|
12
11
|
|
13
12
|
from osism.tasks import reconciler
|
14
|
-
from osism import
|
13
|
+
from osism import utils
|
15
14
|
from osism.services.listener import BaremetalEvents
|
16
15
|
|
17
16
|
|
@@ -78,20 +77,6 @@ logger = logging.getLogger("api")
|
|
78
77
|
baremetal_events = BaremetalEvents()
|
79
78
|
|
80
79
|
|
81
|
-
@app.on_event("startup")
|
82
|
-
async def startup_event():
|
83
|
-
if settings.NETBOX_URL and settings.NETBOX_TOKEN:
|
84
|
-
utils.nb = pynetbox.api(settings.NETBOX_URL, token=settings.NETBOX_TOKEN)
|
85
|
-
|
86
|
-
if settings.IGNORE_SSL_ERRORS:
|
87
|
-
import requests
|
88
|
-
|
89
|
-
requests.packages.urllib3.disable_warnings()
|
90
|
-
session = requests.Session()
|
91
|
-
session.verify = False
|
92
|
-
utils.nb.http_session = session
|
93
|
-
|
94
|
-
|
95
80
|
@app.get("/")
|
96
81
|
async def root():
|
97
82
|
return {"message": "Hello World"}
|
osism/commands/manage.py
CHANGED
@@ -188,7 +188,9 @@ class ImageOctavia(Command):
|
|
188
188
|
"--deactivate",
|
189
189
|
]
|
190
190
|
|
191
|
-
task_signature = openstack.image_manager.si(
|
191
|
+
task_signature = openstack.image_manager.si(
|
192
|
+
*arguments, configs=result, ignore_env=True
|
193
|
+
)
|
192
194
|
task = task_signature.apply_async()
|
193
195
|
if wait:
|
194
196
|
logger.info(
|
osism/commands/server.py
CHANGED
@@ -93,36 +93,131 @@ class ServerMigrate(Command):
|
|
93
93
|
class ServerList(Command):
|
94
94
|
def get_parser(self, prog_name):
|
95
95
|
parser = super(ServerList, self).get_parser(prog_name)
|
96
|
+
parser.add_argument(
|
97
|
+
"--domain",
|
98
|
+
help="List all servers of a specific domain",
|
99
|
+
type=str,
|
100
|
+
default=None,
|
101
|
+
)
|
102
|
+
parser.add_argument(
|
103
|
+
"--project",
|
104
|
+
help="List all servers of a specific project",
|
105
|
+
type=str,
|
106
|
+
default=None,
|
107
|
+
)
|
108
|
+
parser.add_argument(
|
109
|
+
"--project-domain", help="Domain of the project", type=str, default=None
|
110
|
+
)
|
96
111
|
return parser
|
97
112
|
|
98
113
|
def take_action(self, parsed_args):
|
99
114
|
conn = get_cloud_connection()
|
115
|
+
domain = parsed_args.domain
|
116
|
+
project = parsed_args.project
|
117
|
+
project_domain = parsed_args.project_domain
|
100
118
|
|
101
119
|
result = []
|
102
|
-
|
103
|
-
|
104
|
-
|
120
|
+
if domain:
|
121
|
+
_domain = conn.identity.find_domain(domain)
|
122
|
+
if not _domain:
|
123
|
+
logger.error(f"Domain {domain} not found")
|
124
|
+
return
|
125
|
+
projects = list(conn.identity.projects(domain_id=_domain.id))
|
126
|
+
|
127
|
+
for project in projects:
|
128
|
+
query = {"project_id": project.id}
|
129
|
+
for server in conn.compute.servers(all_projects=True, **query):
|
130
|
+
result.append(
|
131
|
+
[
|
132
|
+
project.name,
|
133
|
+
project.id,
|
134
|
+
server.id,
|
135
|
+
server.name,
|
136
|
+
server.flavor["original_name"],
|
137
|
+
server.status,
|
138
|
+
]
|
139
|
+
)
|
140
|
+
|
141
|
+
print(
|
142
|
+
tabulate(
|
143
|
+
result,
|
144
|
+
headers=["Project", "Project ID", "ID", "Name", "Flavor", "Status"],
|
145
|
+
tablefmt="psql",
|
146
|
+
)
|
105
147
|
)
|
106
|
-
|
107
|
-
|
108
|
-
|
148
|
+
|
149
|
+
elif project:
|
150
|
+
if project_domain:
|
151
|
+
_project_domain = conn.identity.find_domain(project_domain)
|
152
|
+
if not _project_domain:
|
153
|
+
logger.error(f"Project domain {project_domain} not found")
|
154
|
+
return
|
155
|
+
query = {"domain_id": _project_domain.id}
|
156
|
+
_project = conn.identity.find_project(project, **query)
|
157
|
+
else:
|
158
|
+
_project = conn.identity.find_project(project)
|
159
|
+
if not _project:
|
160
|
+
logger.error(f"Project {project} not found")
|
161
|
+
return
|
162
|
+
query = {"project_id": _project.id}
|
163
|
+
|
164
|
+
for server in conn.compute.servers(all_projects=True, **query):
|
165
|
+
result.append(
|
166
|
+
[
|
167
|
+
server.id,
|
168
|
+
server.name,
|
169
|
+
server.flavor["original_name"],
|
170
|
+
server.status,
|
171
|
+
]
|
109
172
|
)
|
110
|
-
result.append([server.id, server.name, server.status])
|
111
173
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
logger.info(
|
118
|
-
f"Server {server.id} hangs in ERRORstatus for more than 2 hours"
|
174
|
+
print(
|
175
|
+
tabulate(
|
176
|
+
result,
|
177
|
+
headers=["ID", "Name", "Flavor", "Status"],
|
178
|
+
tablefmt="psql",
|
119
179
|
)
|
120
|
-
|
180
|
+
)
|
121
181
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
182
|
+
else:
|
183
|
+
for server in conn.compute.servers(all_projects=True, status="build"):
|
184
|
+
duration = datetime.now(timezone.utc) - dateutil.parser.parse(
|
185
|
+
server.created_at
|
186
|
+
)
|
187
|
+
if duration.total_seconds() > 7200:
|
188
|
+
logger.info(
|
189
|
+
f"Server {server.id} hangs in BUILD status for more than 2 hours"
|
190
|
+
)
|
191
|
+
result.append(
|
192
|
+
[
|
193
|
+
server.id,
|
194
|
+
server.name,
|
195
|
+
server.flavor["original_name"],
|
196
|
+
server.status,
|
197
|
+
]
|
198
|
+
)
|
199
|
+
|
200
|
+
for server in conn.compute.servers(all_projects=True, status="error"):
|
201
|
+
duration = datetime.now(timezone.utc) - dateutil.parser.parse(
|
202
|
+
server.created_at
|
203
|
+
)
|
204
|
+
if duration.total_seconds() > 7200:
|
205
|
+
logger.info(
|
206
|
+
f"Server {server.id} hangs in ERRORstatus for more than 2 hours"
|
207
|
+
)
|
208
|
+
result.append(
|
209
|
+
[
|
210
|
+
server.id,
|
211
|
+
server.name,
|
212
|
+
server.flavor["original_name"],
|
213
|
+
server.status,
|
214
|
+
]
|
215
|
+
)
|
216
|
+
|
217
|
+
print(
|
218
|
+
tabulate(
|
219
|
+
result,
|
220
|
+
headers=["ID", "Name", "Flavor", "Status"],
|
221
|
+
tablefmt="psql",
|
222
|
+
)
|
127
223
|
)
|
128
|
-
)
|
osism/commands/volume.py
CHANGED
@@ -14,63 +14,160 @@ from osism.commands import get_cloud_connection
|
|
14
14
|
class VolumeList(Command):
|
15
15
|
def get_parser(self, prog_name):
|
16
16
|
parser = super(VolumeList, self).get_parser(prog_name)
|
17
|
+
parser.add_argument(
|
18
|
+
"--domain",
|
19
|
+
help="List all volumes of a specific domain",
|
20
|
+
type=str,
|
21
|
+
default=None,
|
22
|
+
)
|
23
|
+
parser.add_argument(
|
24
|
+
"--project",
|
25
|
+
help="List all volumes of a specific project",
|
26
|
+
type=str,
|
27
|
+
default=None,
|
28
|
+
)
|
29
|
+
parser.add_argument(
|
30
|
+
"--project-domain", help="Domain of the project", type=str, default=None
|
31
|
+
)
|
17
32
|
return parser
|
18
33
|
|
19
34
|
def take_action(self, parsed_args):
|
20
35
|
conn = get_cloud_connection()
|
36
|
+
domain = parsed_args.domain
|
37
|
+
project = parsed_args.project
|
38
|
+
project_domain = parsed_args.project_domain
|
21
39
|
|
22
40
|
result = []
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
if domain:
|
42
|
+
_domain = conn.identity.find_domain(domain)
|
43
|
+
if not _domain:
|
44
|
+
logger.error(f"Domain {domain} not found")
|
45
|
+
return
|
46
|
+
projects = list(conn.identity.projects(domain_id=_domain.id))
|
47
|
+
|
48
|
+
for project in projects:
|
49
|
+
query = {"project_id": project.id}
|
50
|
+
for volume in conn.block_storage.volumes(all_projects=True, **query):
|
51
|
+
result.append(
|
52
|
+
[
|
53
|
+
project.name,
|
54
|
+
project.id,
|
55
|
+
volume.id,
|
56
|
+
volume.name,
|
57
|
+
volume.volume_type,
|
58
|
+
volume.status,
|
59
|
+
]
|
60
|
+
)
|
61
|
+
|
62
|
+
print(
|
63
|
+
tabulate(
|
64
|
+
result,
|
65
|
+
headers=["Project", "Project ID", "ID", "Name", "Type", "Status"],
|
66
|
+
tablefmt="psql",
|
49
67
|
)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
68
|
+
)
|
69
|
+
|
70
|
+
elif project:
|
71
|
+
if project_domain:
|
72
|
+
_project_domain = conn.identity.find_domain(project_domain)
|
73
|
+
if not _project_domain:
|
74
|
+
logger.error(f"Project domain {project_domain} not found")
|
75
|
+
return
|
76
|
+
query = {"domain_id": _project_domain.id}
|
77
|
+
_project = conn.identity.find_project(project, **query)
|
78
|
+
else:
|
79
|
+
_project = conn.identity.find_project(project)
|
80
|
+
if not _project:
|
81
|
+
logger.error(f"Project {project} not found")
|
82
|
+
return
|
83
|
+
query = {"project_id": _project.id}
|
84
|
+
|
85
|
+
for volume in conn.block_storage.volumes(all_projects=True, **query):
|
86
|
+
result.append(
|
87
|
+
[
|
88
|
+
volume.id,
|
89
|
+
volume.name,
|
90
|
+
volume.volume_type,
|
91
|
+
volume.status,
|
92
|
+
]
|
58
93
|
)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
logger.info(
|
66
|
-
f"Volume {volume.id} hangs in ERROR status for more than 2 hours"
|
94
|
+
|
95
|
+
print(
|
96
|
+
tabulate(
|
97
|
+
result,
|
98
|
+
headers=["ID", "Name", "Type", "Status"],
|
99
|
+
tablefmt="psql",
|
67
100
|
)
|
68
|
-
|
101
|
+
)
|
102
|
+
|
103
|
+
else:
|
104
|
+
for volume in conn.block_storage.volumes(
|
105
|
+
all_projects=True, status="detaching"
|
106
|
+
):
|
107
|
+
created_at = pytz.utc.localize(dateutil.parser.parse(volume.created_at))
|
108
|
+
duration = datetime.now(timezone.utc) - created_at
|
109
|
+
if duration.total_seconds() > 7200:
|
110
|
+
logger.info(
|
111
|
+
f"Volume {volume.id} hangs in DETACHING status for more than 2 hours"
|
112
|
+
)
|
113
|
+
result.append(
|
114
|
+
[volume.id, volume.name, volume.volume_type, volume.status]
|
115
|
+
)
|
116
|
+
|
117
|
+
for volume in conn.block_storage.volumes(
|
118
|
+
all_projects=True, status="creating"
|
119
|
+
):
|
120
|
+
created_at = pytz.utc.localize(dateutil.parser.parse(volume.created_at))
|
121
|
+
duration = datetime.now(timezone.utc) - created_at
|
122
|
+
if duration.total_seconds() > 7200:
|
123
|
+
logger.info(
|
124
|
+
f"Volume {volume.id} hangs in CREATING status for more than 2 hours"
|
125
|
+
)
|
126
|
+
result.append(
|
127
|
+
[volume.id, volume.name, volume.volume_type, volume.status]
|
128
|
+
)
|
129
|
+
|
130
|
+
for volume in conn.block_storage.volumes(
|
131
|
+
all_projects=True, status="error_deleting"
|
132
|
+
):
|
133
|
+
created_at = pytz.utc.localize(dateutil.parser.parse(volume.created_at))
|
134
|
+
duration = datetime.now(timezone.utc) - created_at
|
135
|
+
if duration.total_seconds() > 7200:
|
136
|
+
logger.info(
|
137
|
+
f"Volume {volume.id} hangs in ERROR_DELETING status for more than 2 hours"
|
138
|
+
)
|
139
|
+
result.append(
|
140
|
+
[volume.id, volume.name, volume.volume_type, volume.status]
|
141
|
+
)
|
69
142
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
143
|
+
for volume in conn.block_storage.volumes(
|
144
|
+
all_projects=True, status="deleting"
|
145
|
+
):
|
146
|
+
created_at = pytz.utc.localize(dateutil.parser.parse(volume.created_at))
|
147
|
+
duration = datetime.now(timezone.utc) - created_at
|
148
|
+
if duration.total_seconds() > 7200:
|
149
|
+
logger.info(
|
150
|
+
f"Volume {volume.id} hangs in DELETING status for more than 2 hours"
|
151
|
+
)
|
152
|
+
result.append(
|
153
|
+
[volume.id, volume.name, volume.volume_type, volume.status]
|
154
|
+
)
|
155
|
+
|
156
|
+
for volume in conn.block_storage.volumes(all_projects=True, status="error"):
|
157
|
+
created_at = pytz.utc.localize(dateutil.parser.parse(volume.created_at))
|
158
|
+
duration = datetime.now(timezone.utc) - created_at
|
159
|
+
if duration.total_seconds() > 7200:
|
160
|
+
logger.info(
|
161
|
+
f"Volume {volume.id} hangs in ERROR status for more than 2 hours"
|
162
|
+
)
|
163
|
+
result.append(
|
164
|
+
[volume.id, volume.name, volume.volume_type, volume.status]
|
165
|
+
)
|
166
|
+
|
167
|
+
print(
|
168
|
+
tabulate(
|
169
|
+
result,
|
170
|
+
headers=["ID", "Name", "Type", "Status"],
|
171
|
+
tablefmt="psql",
|
172
|
+
)
|
75
173
|
)
|
76
|
-
)
|
osism/settings.py
CHANGED
@@ -34,3 +34,12 @@ INVENTORY_RECONCILER_SCHEDULE = float(
|
|
34
34
|
)
|
35
35
|
|
36
36
|
OSISM_API_URL = os.getenv("OSISM_API_URL", None)
|
37
|
+
|
38
|
+
OSISM_CONDUCTOR_NETBOX_FILTER_LIST = os.getenv(
|
39
|
+
"OSISM_CONDUCTOR_NETBOX_FILTER_LIST",
|
40
|
+
"[{'state': 'active', 'tag': ['managed-by-ironic']}]",
|
41
|
+
)
|
42
|
+
|
43
|
+
NETBOX_SECONDARIES = (
|
44
|
+
os.getenv("NETBOX_SECONDARIES", read_secret("NETBOX_SECONDARIES")) or "[]"
|
45
|
+
)
|
osism/tasks/__init__.py
CHANGED
@@ -183,11 +183,16 @@ def run_command(
|
|
183
183
|
*arguments,
|
184
184
|
publish=True,
|
185
185
|
locking=False,
|
186
|
+
ignore_env=False,
|
186
187
|
auto_release_time=3600,
|
187
188
|
):
|
188
189
|
result = ""
|
189
|
-
|
190
|
-
|
190
|
+
|
191
|
+
if ignore_env:
|
192
|
+
command_env = env
|
193
|
+
else:
|
194
|
+
command_env = os.environ.copy()
|
195
|
+
command_env.update(env)
|
191
196
|
|
192
197
|
if locking:
|
193
198
|
lock = Redlock(
|
osism/tasks/conductor.py
CHANGED
@@ -9,6 +9,7 @@ from loguru import logger
|
|
9
9
|
from pottery import Redlock
|
10
10
|
import yaml
|
11
11
|
|
12
|
+
from osism import settings
|
12
13
|
from osism import utils
|
13
14
|
from osism.tasks import Config, netbox, openstack
|
14
15
|
|
@@ -17,6 +18,7 @@ app.config_from_object(Config)
|
|
17
18
|
|
18
19
|
|
19
20
|
configuration = {}
|
21
|
+
nb_device_query_list = None
|
20
22
|
|
21
23
|
|
22
24
|
@worker_process_init.connect
|
@@ -91,6 +93,49 @@ def celery_init_worker(**kwargs):
|
|
91
93
|
"provisioning_network"
|
92
94
|
] = result.id
|
93
95
|
|
96
|
+
global nb_device_query_list
|
97
|
+
|
98
|
+
try:
|
99
|
+
supported_nb_device_filters = [
|
100
|
+
"site",
|
101
|
+
"region",
|
102
|
+
"site_group",
|
103
|
+
"location",
|
104
|
+
"rack",
|
105
|
+
"tag",
|
106
|
+
"state",
|
107
|
+
]
|
108
|
+
nb_device_query_list = yaml.safe_load(
|
109
|
+
settings.OSISM_CONDUCTOR_NETBOX_FILTER_LIST
|
110
|
+
)
|
111
|
+
if type(nb_device_query_list) is not list:
|
112
|
+
raise TypeError
|
113
|
+
for nb_device_query in nb_device_query_list:
|
114
|
+
if type(nb_device_query) is not dict:
|
115
|
+
raise TypeError
|
116
|
+
for key in list(nb_device_query.keys()):
|
117
|
+
if key not in supported_nb_device_filters:
|
118
|
+
raise ValueError
|
119
|
+
# NOTE: Only "location_id" and "rack_id" are supported by netbox
|
120
|
+
if key in ["location", "rack"]:
|
121
|
+
value_name = nb_device_query.pop(key, "")
|
122
|
+
if key == "location":
|
123
|
+
value_id = netbox.get_location_id(value_name)
|
124
|
+
elif key == "rack":
|
125
|
+
value_id = netbox.get_rack_id(value_name)
|
126
|
+
if value_id:
|
127
|
+
nb_device_query.update({key + "_id": value_id})
|
128
|
+
else:
|
129
|
+
raise ValueError(f"Invalid name {value_name} for {key}")
|
130
|
+
except (yaml.YAMLError, TypeError):
|
131
|
+
logger.error(
|
132
|
+
f"Setting OSISM_CONDUCTOR_NETBOX_FILTER_LIST needs to be an array of mappings containing supported netbox device filters: {supported_nb_device_filters}"
|
133
|
+
)
|
134
|
+
nb_device_query_list = []
|
135
|
+
except ValueError as exc:
|
136
|
+
logger.error(f"Unknown value in OSISM_CONDUCTOR_NETBOX_FILTER_LIST: {exc}")
|
137
|
+
nb_device_query_list = []
|
138
|
+
|
94
139
|
|
95
140
|
@app.on_after_configure.connect
|
96
141
|
def setup_periodic_tasks(sender, **kwargs):
|
@@ -135,10 +180,12 @@ def sync_netbox_with_ironic(self, force_update=False):
|
|
135
180
|
},
|
136
181
|
}
|
137
182
|
|
138
|
-
devices =
|
183
|
+
devices = set()
|
184
|
+
for nb_device_query in nb_device_query_list:
|
185
|
+
devices |= set(netbox.get_devices(**nb_device_query))
|
139
186
|
|
140
187
|
# NOTE: Find nodes in Ironic which are no longer present in netbox and remove them
|
141
|
-
device_names =
|
188
|
+
device_names = {dev.name for dev in devices}
|
142
189
|
nodes = openstack.baremetal_node_list()
|
143
190
|
for node in nodes:
|
144
191
|
logger.info(f"Looking for {node['Name']} in netbox")
|
@@ -151,11 +198,6 @@ def sync_netbox_with_ironic(self, force_update=False):
|
|
151
198
|
logger.info(
|
152
199
|
f"Cleaning up baremetal node not found in netbox: {node['Name']}"
|
153
200
|
)
|
154
|
-
flavor_name = "osism-" + node["Name"]
|
155
|
-
flavor = openstack.compute_flavor_get(flavor_name)
|
156
|
-
if flavor:
|
157
|
-
logger.info(f"Deleting flavor {flavor_name}")
|
158
|
-
openstack.compute_flavor_delete(flavor)
|
159
201
|
for port in openstack.baremetal_port_list(
|
160
202
|
details=False, attributes=dict(node_uuid=node["UUID"])
|
161
203
|
):
|
@@ -224,19 +266,6 @@ def sync_netbox_with_ironic(self, force_update=False):
|
|
224
266
|
for interface in node_interfaces
|
225
267
|
if interface.enabled and not interface.mgmt_only and interface.mac_address
|
226
268
|
]
|
227
|
-
flavor_attributes = {
|
228
|
-
"ram": 1,
|
229
|
-
"disk": 0,
|
230
|
-
"vcpus": 1,
|
231
|
-
"is_public": False,
|
232
|
-
"extra_specs": {
|
233
|
-
"resources:CUSTOM_"
|
234
|
-
+ device.name.upper().replace("-", "_").replace(".", "_"): "1",
|
235
|
-
"resources:VCPU": "0",
|
236
|
-
"resources:MEMORY_MB": "0",
|
237
|
-
"resources:DISK_GB": "0",
|
238
|
-
},
|
239
|
-
}
|
240
269
|
|
241
270
|
lock = Redlock(
|
242
271
|
key=f"lock_osism_tasks_conductor_sync_netbox_with_ironic-{device.name}",
|
@@ -366,48 +395,6 @@ def sync_netbox_with_ironic(self, force_update=False):
|
|
366
395
|
logger.info(
|
367
396
|
f"Validation of management interface failed for baremetal node for {device.name}\nReason: {node_validation['management'].reason}"
|
368
397
|
)
|
369
|
-
|
370
|
-
flavor_name = "osism-" + device.name
|
371
|
-
flavor = openstack.compute_flavor_get(flavor_name)
|
372
|
-
if not flavor:
|
373
|
-
logger.info(f"Creating flavor for {flavor_name}")
|
374
|
-
flavor = openstack.compute_flavor_create(
|
375
|
-
flavor_name, flavor_attributes
|
376
|
-
)
|
377
|
-
else:
|
378
|
-
flavor_updates = {}
|
379
|
-
deep_compare(flavor_attributes, flavor, flavor_updates)
|
380
|
-
flavor_updates_extra_specs = flavor_updates.pop("extra_specs", None)
|
381
|
-
if flavor_updates:
|
382
|
-
logger.info(
|
383
|
-
f"Updating flavor for {device.name} with {flavor_updates}"
|
384
|
-
)
|
385
|
-
openstack.compute_flavor_delete(flavor)
|
386
|
-
flavor = openstack.compute_flavor_create(
|
387
|
-
flavor_name, flavor_attributes
|
388
|
-
)
|
389
|
-
elif flavor_updates_extra_specs:
|
390
|
-
logger.info(
|
391
|
-
f"Updating flavor extra_specs for {device.name} with {flavor_updates_extra_specs}"
|
392
|
-
)
|
393
|
-
openstack.compute_flavor_update_extra_specs(
|
394
|
-
flavor, flavor_updates_extra_specs
|
395
|
-
)
|
396
|
-
flavor = openstack.compute_flavor_get(flavor_name)
|
397
|
-
for extra_specs_key in flavor["extra_specs"].keys():
|
398
|
-
if (
|
399
|
-
extra_specs_key
|
400
|
-
not in flavor_attributes["extra_specs"].keys()
|
401
|
-
):
|
402
|
-
logger.info(
|
403
|
-
f"Deleting flavor extra_specs property {extra_specs_key} for {device.name}"
|
404
|
-
)
|
405
|
-
flavor = (
|
406
|
-
openstack.compute_flavor_delete_extra_specs_property(
|
407
|
-
flavor, extra_specs_key
|
408
|
-
)
|
409
|
-
)
|
410
|
-
|
411
398
|
except Exception as exc:
|
412
399
|
logger.info(
|
413
400
|
f"Could not fully synchronize device {device.name} with ironic: {exc}"
|
osism/tasks/netbox.py
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
2
2
|
|
3
3
|
from celery import Celery
|
4
|
-
from celery.signals import worker_process_init
|
5
4
|
from loguru import logger
|
6
5
|
from pottery import Redlock
|
7
|
-
import pynetbox
|
8
6
|
|
9
7
|
from osism import settings, utils
|
10
8
|
from osism.tasks import Config, run_command
|
@@ -13,20 +11,6 @@ app = Celery("netbox")
|
|
13
11
|
app.config_from_object(Config)
|
14
12
|
|
15
13
|
|
16
|
-
@worker_process_init.connect
|
17
|
-
def celery_init_worker(**kwargs):
|
18
|
-
if settings.NETBOX_URL and settings.NETBOX_TOKEN:
|
19
|
-
utils.nb = pynetbox.api(settings.NETBOX_URL, token=settings.NETBOX_TOKEN)
|
20
|
-
|
21
|
-
if settings.IGNORE_SSL_ERRORS:
|
22
|
-
import requests
|
23
|
-
|
24
|
-
requests.packages.urllib3.disable_warnings()
|
25
|
-
session = requests.Session()
|
26
|
-
session.verify = False
|
27
|
-
utils.nb.http_session = session
|
28
|
-
|
29
|
-
|
30
14
|
@app.on_after_configure.connect
|
31
15
|
def setup_periodic_tasks(sender, **kwargs):
|
32
16
|
pass
|
@@ -37,6 +21,9 @@ def run(self, action, arguments):
|
|
37
21
|
pass
|
38
22
|
|
39
23
|
|
24
|
+
# NOTE: While `get_*` tasks only operate on the netbox configured in NETBOX_URL, `set_*` tasks additionally operate on all netbox instances listed in NETBOX_SECONDARIES
|
25
|
+
|
26
|
+
|
40
27
|
@app.task(bind=True, name="osism.tasks.netbox.set_maintenance")
|
41
28
|
def set_maintenance(self, device_name, state=True):
|
42
29
|
"""Set the maintenance state for a device in the Netbox."""
|
@@ -48,14 +35,18 @@ def set_maintenance(self, device_name, state=True):
|
|
48
35
|
)
|
49
36
|
if lock.acquire(timeout=20):
|
50
37
|
try:
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
device.
|
56
|
-
device
|
57
|
-
|
58
|
-
|
38
|
+
for nb in [utils.nb] + utils.secondary_nb_list:
|
39
|
+
logger.info(
|
40
|
+
f"Set maintenance state of device {device_name} = {state} on {nb.base_url}"
|
41
|
+
)
|
42
|
+
device = nb.dcim.devices.get(name=device_name)
|
43
|
+
if device:
|
44
|
+
device.custom_fields.update({"maintenance": state})
|
45
|
+
device.save()
|
46
|
+
else:
|
47
|
+
logger.error(
|
48
|
+
f"Could not set maintenance for {device_name} on {nb.base_url}"
|
49
|
+
)
|
59
50
|
finally:
|
60
51
|
lock.release()
|
61
52
|
else:
|
@@ -73,14 +64,19 @@ def set_provision_state(self, device_name, state):
|
|
73
64
|
)
|
74
65
|
if lock.acquire(timeout=20):
|
75
66
|
try:
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
device.
|
82
|
-
|
83
|
-
|
67
|
+
|
68
|
+
for nb in [utils.nb] + utils.secondary_nb_list:
|
69
|
+
logger.info(
|
70
|
+
f"Set provision state of device {device_name} = {state} on {nb.base_url}"
|
71
|
+
)
|
72
|
+
device = nb.dcim.devices.get(name=device_name)
|
73
|
+
if device:
|
74
|
+
device.custom_fields.update({"provision_state": state})
|
75
|
+
device.save()
|
76
|
+
else:
|
77
|
+
logger.error(
|
78
|
+
f"Could not set provision state for {device_name} on {nb.base_url}"
|
79
|
+
)
|
84
80
|
finally:
|
85
81
|
lock.release()
|
86
82
|
else:
|
@@ -98,26 +94,54 @@ def set_power_state(self, device_name, state):
|
|
98
94
|
)
|
99
95
|
if lock.acquire(timeout=20):
|
100
96
|
try:
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
device.
|
106
|
-
device
|
107
|
-
|
108
|
-
|
97
|
+
for nb in [utils.nb] + utils.secondary_nb_list:
|
98
|
+
logger.info(
|
99
|
+
f"Set power state of device {device_name} = {state} on {nb.base_url}"
|
100
|
+
)
|
101
|
+
device = nb.dcim.devices.get(name=device_name)
|
102
|
+
if device:
|
103
|
+
device.custom_fields.update({"power_state": state})
|
104
|
+
device.save()
|
105
|
+
else:
|
106
|
+
logger.error(
|
107
|
+
f"Could not set power state for {device_name} on {nb.base_url}"
|
108
|
+
)
|
109
109
|
finally:
|
110
110
|
lock.release()
|
111
111
|
else:
|
112
112
|
logger.error("Could not acquire lock for node {device_name}")
|
113
113
|
|
114
114
|
|
115
|
-
@app.task(bind=True, name="osism.tasks.netbox.
|
116
|
-
def
|
117
|
-
|
115
|
+
@app.task(bind=True, name="osism.tasks.netbox.get_location_id")
|
116
|
+
def get_location_id(self, location_name):
|
117
|
+
try:
|
118
|
+
location = utils.nb.dcim.locations.get(name=location_name)
|
119
|
+
except ValueError:
|
120
|
+
return None
|
121
|
+
if location:
|
122
|
+
return location.id
|
123
|
+
else:
|
124
|
+
return None
|
125
|
+
|
126
|
+
|
127
|
+
@app.task(bind=True, name="osism.tasks.netbox.get_rack_id")
|
128
|
+
def get_rack_id(self, rack_name):
|
129
|
+
try:
|
130
|
+
rack = utils.nb.dcim.racks.get(name=rack_name)
|
131
|
+
except ValueError:
|
132
|
+
return None
|
133
|
+
if rack:
|
134
|
+
return rack.id
|
135
|
+
else:
|
136
|
+
return None
|
118
137
|
|
119
138
|
|
120
139
|
@app.task(bind=True, name="osism.tasks.netbox.get_devices")
|
140
|
+
def get_devices(self, **query):
|
141
|
+
return utils.nb.dcim.devices.filter(**query)
|
142
|
+
|
143
|
+
|
144
|
+
@app.task(bind=True, name="osism.tasks.netbox.get_device_by_name")
|
121
145
|
def get_device_by_name(self, name):
|
122
146
|
return utils.nb.dcim.devices.get(name=name)
|
123
147
|
|
osism/tasks/openstack.py
CHANGED
@@ -136,50 +136,15 @@ def baremetal_port_delete(self, port_or_id):
|
|
136
136
|
return result
|
137
137
|
|
138
138
|
|
139
|
-
@app.task(bind=True, name="osism.tasks.openstack.compute_flavor_get")
|
140
|
-
def compute_flavor_get(self, name_or_id):
|
141
|
-
conn = utils.get_openstack_connection()
|
142
|
-
result = conn.compute.find_flavor(
|
143
|
-
name_or_id, ignore_missing=True, get_extra_specs=True
|
144
|
-
)
|
145
|
-
return result
|
146
|
-
|
147
|
-
|
148
|
-
@app.task(bind=True, name="osism.tasks.openstack.compute_flavor_create")
|
149
|
-
def compute_flavor_create(self, name, attributes=None):
|
150
|
-
if attributes is None:
|
151
|
-
attributes = {}
|
152
|
-
attributes.update({"name": name})
|
153
|
-
extra_specs = attributes.pop("extra_specs", None)
|
154
|
-
conn = utils.get_openstack_connection()
|
155
|
-
flavor = conn.compute.create_flavor(**attributes)
|
156
|
-
if extra_specs:
|
157
|
-
flavor = conn.compute.create_flavor_extra_specs(flavor, extra_specs)
|
158
|
-
return flavor
|
159
|
-
|
160
|
-
|
161
|
-
@app.task(bind=True, name="osism.tasks.openstack.compute_flavor_delete")
|
162
|
-
def compute_flavor_delete(self, flavor):
|
163
|
-
conn = utils.get_openstack_connection()
|
164
|
-
conn.compute.delete_flavor(flavor, ignore_missing=True)
|
165
|
-
|
166
|
-
|
167
|
-
@app.task(bind=True, name="osism.tasks.openstack.compute_flavor_update_extra_specs")
|
168
|
-
def compute_flavor_update_extra_specs(self, flavor, extra_specs={}):
|
169
|
-
conn = utils.get_openstack_connection()
|
170
|
-
for key, value in extra_specs.items():
|
171
|
-
conn.compute.update_flavor_extra_specs_property(flavor, key, value)
|
172
|
-
|
173
|
-
|
174
|
-
@app.task(bind=True, name="osism.tasks.openstack.compute_flavor_delete_extra_specs")
|
175
|
-
def compute_flavor_delete_extra_specs_property(self, flavor, prop):
|
176
|
-
conn = utils.get_openstack_connection()
|
177
|
-
conn.compute.delete_flavor_extra_specs_property(flavor, prop)
|
178
|
-
|
179
|
-
|
180
139
|
@app.task(bind=True, name="osism.tasks.openstack.image_manager")
|
181
140
|
def image_manager(
|
182
|
-
self,
|
141
|
+
self,
|
142
|
+
*arguments,
|
143
|
+
configs=None,
|
144
|
+
publish=True,
|
145
|
+
locking=False,
|
146
|
+
auto_release_time=3600,
|
147
|
+
ignore_env=False
|
183
148
|
):
|
184
149
|
command = "/usr/local/bin/openstack-image-manager"
|
185
150
|
if configs:
|
@@ -209,6 +174,7 @@ def image_manager(
|
|
209
174
|
publish=publish,
|
210
175
|
locking=locking,
|
211
176
|
auto_release_time=auto_release_time,
|
177
|
+
ignore_env=ignore_env,
|
212
178
|
)
|
213
179
|
return rc
|
214
180
|
else:
|
@@ -220,6 +186,7 @@ def image_manager(
|
|
220
186
|
publish=publish,
|
221
187
|
locking=locking,
|
222
188
|
auto_release_time=auto_release_time,
|
189
|
+
ignore_env=ignore_env,
|
223
190
|
)
|
224
191
|
|
225
192
|
|
osism/utils/__init__.py
CHANGED
@@ -1,13 +1,34 @@
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
2
2
|
|
3
3
|
import keystoneauth1
|
4
|
+
from loguru import logger
|
4
5
|
import openstack
|
5
6
|
import pynetbox
|
6
7
|
from redis import Redis
|
7
8
|
import urllib3
|
9
|
+
import yaml
|
8
10
|
|
9
11
|
from osism import settings
|
10
12
|
|
13
|
+
|
14
|
+
def get_netbox_connection(netbox_url, netbox_token, ignore_ssl_errors=False):
|
15
|
+
if netbox_url and netbox_token:
|
16
|
+
nb = pynetbox.api(netbox_url, token=netbox_token)
|
17
|
+
|
18
|
+
if ignore_ssl_errors and nb:
|
19
|
+
import requests
|
20
|
+
|
21
|
+
urllib3.disable_warnings()
|
22
|
+
session = requests.Session()
|
23
|
+
session.verify = False
|
24
|
+
nb.http_session = session
|
25
|
+
|
26
|
+
else:
|
27
|
+
nb = None
|
28
|
+
|
29
|
+
return nb
|
30
|
+
|
31
|
+
|
11
32
|
redis = Redis(
|
12
33
|
host=settings.REDIS_HOST,
|
13
34
|
port=settings.REDIS_PORT,
|
@@ -16,19 +37,53 @@ redis = Redis(
|
|
16
37
|
)
|
17
38
|
redis.ping()
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
if settings.IGNORE_SSL_ERRORS and nb:
|
23
|
-
import requests
|
24
|
-
|
25
|
-
urllib3.disable_warnings()
|
26
|
-
session = requests.Session()
|
27
|
-
session.verify = False
|
28
|
-
nb.http_session = session
|
40
|
+
nb = get_netbox_connection(
|
41
|
+
settings.NETBOX_URL, settings.NETBOX_TOKEN, settings.IGNORE_SSL_ERRORS
|
42
|
+
)
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
try:
|
45
|
+
secondary_nb_settings_list = yaml.safe_load(settings.NETBOX_SECONDARIES)
|
46
|
+
supported_secondary_nb_keys = ["NETBOX_URL", "NETBOX_TOKEN", "IGNORE_SSL_ERRORS"]
|
47
|
+
secondary_nb_list = []
|
48
|
+
if type(secondary_nb_settings_list) is not list:
|
49
|
+
raise TypeError(
|
50
|
+
f"Setting NETBOX_SECONDARIES needs to be an array of mappings containing supported netbox API configuration: {supported_secondary_nb_keys}"
|
51
|
+
)
|
52
|
+
for secondary_nb_settings in secondary_nb_settings_list:
|
53
|
+
if type(secondary_nb_settings) is not dict:
|
54
|
+
raise TypeError(
|
55
|
+
f"Elements in setting NETBOX_SECONDARIES need to be mappings containing supported netbox API configuration: {supported_secondary_nb_keys}"
|
56
|
+
)
|
57
|
+
for key in list(secondary_nb_settings.keys()):
|
58
|
+
if key not in supported_secondary_nb_keys:
|
59
|
+
raise ValueError(
|
60
|
+
f"Unknown key in element of setting NETBOX_SECONDARIES. Supported keys: {supported_secondary_nb_keys}"
|
61
|
+
)
|
62
|
+
if (
|
63
|
+
"NETBOX_URL" not in secondary_nb_settings
|
64
|
+
or not secondary_nb_settings["NETBOX_URL"]
|
65
|
+
):
|
66
|
+
raise ValueError(
|
67
|
+
"All NETBOX_URL values in the elements of setting NETBOX_SECONDARIES need to be valid netbox URLs"
|
68
|
+
)
|
69
|
+
if (
|
70
|
+
"NETBOX_TOKEN" not in secondary_nb_settings
|
71
|
+
or not secondary_nb_settings["NETBOX_TOKEN"]
|
72
|
+
):
|
73
|
+
raise ValueError(
|
74
|
+
"All NETBOX_TOKEN values in the elements of setting NETBOX_SECONDARIES need to be valid netbox tokens"
|
75
|
+
)
|
76
|
+
|
77
|
+
secondary_nb_list.append(
|
78
|
+
get_netbox_connection(
|
79
|
+
secondary_nb_settings["NETBOX_URL"],
|
80
|
+
secondary_nb_settings["NETBOX_TOKEN"],
|
81
|
+
secondary_nb_settings.get("IGNORE_SSL_ERRORS", True),
|
82
|
+
)
|
83
|
+
)
|
84
|
+
except (yaml.YAMLError, TypeError, ValueError) as exc:
|
85
|
+
logger.error(f"Error parsing settings NETBOX_SECONDARIES: {exc}")
|
86
|
+
secondary_nb_list = []
|
32
87
|
|
33
88
|
|
34
89
|
def get_openstack_connection():
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: osism
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.20250505.0
|
4
4
|
Summary: OSISM manager interface
|
5
5
|
Home-page: https://github.com/osism/python-osism
|
6
6
|
Author: OSISM GmbH
|
@@ -27,7 +27,7 @@ Requires-Dist: GitPython==3.1.44
|
|
27
27
|
Requires-Dist: Jinja2==3.1.6
|
28
28
|
Requires-Dist: PyYAML==6.0.2
|
29
29
|
Requires-Dist: ara==1.7.2
|
30
|
-
Requires-Dist: celery[redis]==5.5.
|
30
|
+
Requires-Dist: celery[redis]==5.5.2
|
31
31
|
Requires-Dist: cliff==4.9.1
|
32
32
|
Requires-Dist: deepdiff==8.4.2
|
33
33
|
Requires-Dist: docker==7.1.0
|
@@ -37,31 +37,31 @@ Requires-Dist: flower==2.0.1
|
|
37
37
|
Requires-Dist: hiredis==3.1.0
|
38
38
|
Requires-Dist: jc==1.25.4
|
39
39
|
Requires-Dist: keystoneauth1==5.10.0
|
40
|
-
Requires-Dist: kombu==5.5.
|
40
|
+
Requires-Dist: kombu==5.5.3
|
41
41
|
Requires-Dist: kubernetes==32.0.1
|
42
42
|
Requires-Dist: loguru==0.7.3
|
43
43
|
Requires-Dist: netmiko==4.5.0
|
44
44
|
Requires-Dist: nornir-ansible==2023.12.28
|
45
45
|
Requires-Dist: nornir==3.5.0
|
46
|
-
Requires-Dist: openstacksdk==4.
|
46
|
+
Requires-Dist: openstacksdk==4.5.0
|
47
47
|
Requires-Dist: pottery==3.0.1
|
48
|
-
Requires-Dist: prompt-toolkit==3.0.
|
49
|
-
Requires-Dist: pydantic==1.10.
|
48
|
+
Requires-Dist: prompt-toolkit==3.0.51
|
49
|
+
Requires-Dist: pydantic==1.10.22
|
50
50
|
Requires-Dist: pynetbox==7.4.1
|
51
51
|
Requires-Dist: pytest-testinfra==10.2.2
|
52
52
|
Requires-Dist: python-dateutil==2.9.0.post0
|
53
|
-
Requires-Dist: setuptools==
|
53
|
+
Requires-Dist: setuptools==80.3.1
|
54
54
|
Requires-Dist: sqlmodel==0.0.24
|
55
55
|
Requires-Dist: sushy==5.5.0
|
56
56
|
Requires-Dist: tabulate==0.9.0
|
57
57
|
Requires-Dist: transitions==0.9.2
|
58
|
-
Requires-Dist: uvicorn[standard]==0.34.
|
58
|
+
Requires-Dist: uvicorn[standard]==0.34.2
|
59
59
|
Requires-Dist: watchdog==6.0.0
|
60
60
|
Provides-Extra: ansible
|
61
61
|
Requires-Dist: ansible-runner==2.4.1; extra == "ansible"
|
62
|
-
Requires-Dist: ansible-core==2.18.
|
62
|
+
Requires-Dist: ansible-core==2.18.5; extra == "ansible"
|
63
63
|
Provides-Extra: openstack-image-manager
|
64
|
-
Requires-Dist: openstack-image-manager==0.
|
64
|
+
Requires-Dist: openstack-image-manager==0.20250423.0; extra == "openstack-image-manager"
|
65
65
|
Dynamic: author
|
66
66
|
Dynamic: author-email
|
67
67
|
Dynamic: classifier
|
@@ -1,8 +1,8 @@
|
|
1
1
|
osism/__init__.py,sha256=1UiNTBus0V0f2AbZQzAtVtu6zkfCCrw0OTq--NwFAqY,341
|
2
2
|
osism/__main__.py,sha256=ILe4gu61xEISiBsxanqTQIdSkV-YhpZXTRlguCYyssk,141
|
3
|
-
osism/api.py,sha256=
|
3
|
+
osism/api.py,sha256=xJC6RyC1TU54PU2C06rlialh2SmTCgM1L0MSgyUlubU,4331
|
4
4
|
osism/main.py,sha256=Dt2-9sLXcS-Ny4DAz7hrha-KRc7zd7BFUTRdfs_X8z4,893
|
5
|
-
osism/settings.py,sha256=
|
5
|
+
osism/settings.py,sha256=xzFRbf5mdl_MUAUFqPavE14vaVuTr5z6b969vJQvF2E,1306
|
6
6
|
osism/actions/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
|
7
7
|
osism/commands/__init__.py,sha256=Ag4wX_DCgXRdoLn6t069jqb3DdRylsX2nyYkiyCx4uk,456
|
8
8
|
osism/commands/apply.py,sha256=n3lLb1cS3GahQqRT0723di98hg47MjVzDzkAoeZX7qU,16780
|
@@ -13,11 +13,11 @@ osism/commands/console.py,sha256=8BPz1hio5Wi6kONVAWFuSqkDRrMcLEYeFIY8dbtN6e4,321
|
|
13
13
|
osism/commands/container.py,sha256=Fku2GaCM3Idq_FxExUtNqjrEM0XYjpVvXmueSVO8S_c,1601
|
14
14
|
osism/commands/get.py,sha256=ryytjtXWmlMV0NucP5tGkMZu0nIlC4xVtjRk4iMZ06c,8967
|
15
15
|
osism/commands/log.py,sha256=2IpYuosC7FZwwLvM8HmKSU1NRNIelVVYzqjjVMCrOJk,4072
|
16
|
-
osism/commands/manage.py,sha256=
|
16
|
+
osism/commands/manage.py,sha256=5ypZzA91QGgWCt35rVNJSXVC9l189IntS4UnsD8LRZY,11971
|
17
17
|
osism/commands/netbox.py,sha256=_2-j6XM9JvH0DXnbct6rG9T6hT8KEpm3vazQC28Rt7I,4529
|
18
18
|
osism/commands/noset.py,sha256=7zDFuFMyNpo7DUOKcNiYV8nodtdMOYFp5LDPcuJhlZ8,1481
|
19
19
|
osism/commands/reconciler.py,sha256=Ja_b86gX6-_Pr3DmrUUvskmEnnJpHQ-XJNQLycMJeyc,2818
|
20
|
-
osism/commands/server.py,sha256=
|
20
|
+
osism/commands/server.py,sha256=avmoOv5rjOi-fN2A-27cPwOtiy2Q2j6UFtCh3QrfWAI,7512
|
21
21
|
osism/commands/service.py,sha256=A1lgAlGeCJpbFFqF55DRWPcCirIgpU0dzjzVLZ0mz3k,2649
|
22
22
|
osism/commands/set.py,sha256=xLBi2DzbVQo2jb3-cOIE9In5UB3vFxquQJkDN-EsfhM,1425
|
23
23
|
osism/commands/status.py,sha256=X-Rcj-XuNPDBoxsGkf96NswwpmTognxz1V6E2NX2ZgY,1997
|
@@ -25,7 +25,7 @@ osism/commands/sync.py,sha256=Vf9k7uVQTIu-8kK1u7Gjs3et3RRBEkmnNikot_PFJIE,484
|
|
25
25
|
osism/commands/task.py,sha256=mwJJ7a71Lw3o_FX7j3rR0-NbPdPwMDOjbOAiiXE4uGc,543
|
26
26
|
osism/commands/validate.py,sha256=hIQB0zk4xIBZJORtBp_tWrXTRKKhB2qi6j-mznDxKR4,4191
|
27
27
|
osism/commands/vault.py,sha256=Ip0IMR7zaBkPbLJenXr4ZwxM6FnozZ9wn9rwHmFHo8s,1818
|
28
|
-
osism/commands/volume.py,sha256=
|
28
|
+
osism/commands/volume.py,sha256=l6oAk__dFM8KKdLTWOvuSiI7tLh9wAPZp8hwmYF-NX0,6595
|
29
29
|
osism/commands/wait.py,sha256=mKFDqEXcaLlKw1T3MuBEZpNh7CeL3lpUXgubD2_f8es,6580
|
30
30
|
osism/commands/worker.py,sha256=iraCOEhCp7WgfjfZ0-12XQYQPUjpi9rSJK5Z9JfNJk4,1651
|
31
31
|
osism/core/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
|
@@ -35,21 +35,21 @@ osism/data/__init__.py,sha256=izXdh0J3vPLQI7kBhJI7ibJQzPqU_nlONP0L4Cf_k6A,1504
|
|
35
35
|
osism/plugins/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
|
36
36
|
osism/services/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
|
37
37
|
osism/services/listener.py,sha256=eEamlQsJqCuU9K2QFmk3yM9LAJZEanVcTLtGMsNCKjs,9783
|
38
|
-
osism/tasks/__init__.py,sha256=
|
38
|
+
osism/tasks/__init__.py,sha256=ZEu_KYsapTYp0etr-rLqie_NT_LndHDDpx53xITru5Y,8691
|
39
39
|
osism/tasks/ansible.py,sha256=RcLxLrjzL5_X6OjNHm3H0lZlmKKlYKIANB0M4_d4chE,1109
|
40
40
|
osism/tasks/ceph.py,sha256=eIQkah3Kj4INtOkF9kTjHbXJ3_J2lg48EWJKfHc-UYw,615
|
41
|
-
osism/tasks/conductor.py,sha256=
|
41
|
+
osism/tasks/conductor.py,sha256=jtvw5UfhPOPEbIBkIyBkiHbaPJsNKbEABbc3FtHYOo4,18210
|
42
42
|
osism/tasks/kolla.py,sha256=wJQpWn_01iWLkr7l7T7RNrQGfRgsgmYi4WQlTmNGvew,618
|
43
43
|
osism/tasks/kubernetes.py,sha256=VzXq_VrYU_CLm4cOruqnE3Kq2ydfO9glZ3p0bp3OYoc,625
|
44
|
-
osism/tasks/netbox.py,sha256=
|
45
|
-
osism/tasks/openstack.py,sha256=
|
44
|
+
osism/tasks/netbox.py,sha256=QVOLiTH2Su237YAS0QfXbQ86E-OA1JzrFDfyi9JBmvk,5658
|
45
|
+
osism/tasks/openstack.py,sha256=g15tCll5vP1pC6ysxRCTZxplsdGmXbxaCH3k1Qdv5Xg,6367
|
46
46
|
osism/tasks/reconciler.py,sha256=RGUcax2gDuyVLw1nGRQn5izXclnPBo9MRl0ndLDiiYQ,2707
|
47
|
-
osism/utils/__init__.py,sha256=
|
48
|
-
osism-0.
|
49
|
-
osism-0.
|
50
|
-
osism-0.
|
51
|
-
osism-0.
|
52
|
-
osism-0.
|
53
|
-
osism-0.
|
54
|
-
osism-0.
|
55
|
-
osism-0.
|
47
|
+
osism/utils/__init__.py,sha256=_uhe9ghqAJ2me0p187X-vzJ2Nh_Hpmfw3D6HU4kKa10,3759
|
48
|
+
osism-0.20250505.0.dist-info/licenses/AUTHORS,sha256=DJIRsjyrFxKjFvmpUNDRDBS04nRiJ5B6FpKcDcfnoGM,36
|
49
|
+
osism-0.20250505.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
50
|
+
osism-0.20250505.0.dist-info/METADATA,sha256=9UPuB22oJMzuSpvDxVYVXTun60Wz4uBvtE7fMUKJKBY,2972
|
51
|
+
osism-0.20250505.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
52
|
+
osism-0.20250505.0.dist-info/entry_points.txt,sha256=DlfrvU14rI55WuTrwNRoce9FY3ric4HeZKZx_Z3NzCw,3015
|
53
|
+
osism-0.20250505.0.dist-info/pbr.json,sha256=xWRK5iE40MX99mSkANXdqP33gzUrHwuP4JmJG0hUNMA,47
|
54
|
+
osism-0.20250505.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
|
55
|
+
osism-0.20250505.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
janhorstmann <horstmann@osism.tech>
|
@@ -0,0 +1 @@
|
|
1
|
+
{"git_version": "6139ca4", "is_release": false}
|
@@ -1 +0,0 @@
|
|
1
|
-
renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
|
@@ -1 +0,0 @@
|
|
1
|
-
{"git_version": "976a50a", "is_release": false}
|
File without changes
|
File without changes
|
File without changes
|