osism 0.20250525.0__py3-none-any.whl → 0.20250530.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/commands/baremetal.py +237 -0
- osism/commands/netbox.py +1 -1
- osism/tasks/conductor.py +22 -0
- osism/tasks/netbox.py +5 -0
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/METADATA +2 -2
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/RECORD +12 -11
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/WHEEL +1 -1
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/entry_points.txt +3 -0
- osism-0.20250530.0.dist-info/pbr.json +1 -0
- osism-0.20250525.0.dist-info/pbr.json +0 -1
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/licenses/AUTHORS +0 -0
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/licenses/LICENSE +0 -0
- {osism-0.20250525.0.dist-info → osism-0.20250530.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,237 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
from cliff.command import Command
|
4
|
+
|
5
|
+
from loguru import logger
|
6
|
+
import openstack
|
7
|
+
from tabulate import tabulate
|
8
|
+
import json
|
9
|
+
|
10
|
+
from osism.commands import get_cloud_connection
|
11
|
+
|
12
|
+
|
13
|
+
class BaremetalList(Command):
|
14
|
+
def get_parser(self, prog_name):
|
15
|
+
parser = super(BaremetalList, self).get_parser(prog_name)
|
16
|
+
parser.add_argument(
|
17
|
+
"--provision-state",
|
18
|
+
default=None,
|
19
|
+
choices=["enroll", "managable", "available", "active", "error"],
|
20
|
+
type=str,
|
21
|
+
help="Only list nodes with the given provision state",
|
22
|
+
)
|
23
|
+
parser.add_argument(
|
24
|
+
"--maintenance",
|
25
|
+
default=False,
|
26
|
+
action="store_true",
|
27
|
+
help="Only list baremetal nodes in maintenance mode",
|
28
|
+
)
|
29
|
+
return parser
|
30
|
+
|
31
|
+
def take_action(self, parsed_args):
|
32
|
+
provision_state = parsed_args.provision_state
|
33
|
+
maintenance = parsed_args.maintenance
|
34
|
+
|
35
|
+
conn = get_cloud_connection()
|
36
|
+
|
37
|
+
query = {}
|
38
|
+
if provision_state:
|
39
|
+
query.update(dict(provision_state=provision_state))
|
40
|
+
if maintenance:
|
41
|
+
query.update(dict(maintenance=maintenance))
|
42
|
+
|
43
|
+
baremetal = conn.baremetal.nodes(**query)
|
44
|
+
|
45
|
+
result = [
|
46
|
+
[
|
47
|
+
b["name"],
|
48
|
+
b["power_state"],
|
49
|
+
b["provision_state"],
|
50
|
+
b["maintenance"],
|
51
|
+
]
|
52
|
+
for b in baremetal
|
53
|
+
]
|
54
|
+
|
55
|
+
print(
|
56
|
+
tabulate(
|
57
|
+
result,
|
58
|
+
headers=[
|
59
|
+
"Name",
|
60
|
+
"Power State",
|
61
|
+
"Provision State",
|
62
|
+
"Maintenance",
|
63
|
+
],
|
64
|
+
tablefmt="psql",
|
65
|
+
)
|
66
|
+
)
|
67
|
+
|
68
|
+
|
69
|
+
class BaremetalDeploy(Command):
|
70
|
+
def get_parser(self, prog_name):
|
71
|
+
parser = super(BaremetalDeploy, self).get_parser(prog_name)
|
72
|
+
|
73
|
+
parser_exc_group = parser.add_mutually_exclusive_group(required=True)
|
74
|
+
parser_exc_group.add_argument(
|
75
|
+
"--all",
|
76
|
+
default=False,
|
77
|
+
help="Deploy all baremetal nodes in provision state available",
|
78
|
+
action="store_true",
|
79
|
+
)
|
80
|
+
parser_exc_group.add_argument(
|
81
|
+
"--name",
|
82
|
+
default=[],
|
83
|
+
help="Deploy given baremetal node when in provision state available. May be specified multiple times",
|
84
|
+
action="append",
|
85
|
+
)
|
86
|
+
parser.add_argument(
|
87
|
+
"--rebuild",
|
88
|
+
default=False,
|
89
|
+
help="Rebuild given nodes in active state",
|
90
|
+
action="store_true",
|
91
|
+
)
|
92
|
+
parser.add_argument(
|
93
|
+
"--yes-i-really-really-mean-it",
|
94
|
+
default=False,
|
95
|
+
help="Specify this in connection with '--rebuild --all' to actually rebuild all nodes",
|
96
|
+
action="store_true",
|
97
|
+
)
|
98
|
+
return parser
|
99
|
+
|
100
|
+
def take_action(self, parsed_args):
|
101
|
+
all_nodes = parsed_args.all
|
102
|
+
names = parsed_args.name
|
103
|
+
rebuild = parsed_args.rebuild
|
104
|
+
yes_i_really_really_mean_it = parsed_args.yes_i_really_really_mean_it
|
105
|
+
|
106
|
+
if all_nodes and rebuild and not yes_i_really_really_mean_it:
|
107
|
+
logger.error(
|
108
|
+
"Please confirm that you wish to rebuild all nodes by specifying '--yes-i-really-really-mean-it'"
|
109
|
+
)
|
110
|
+
return
|
111
|
+
|
112
|
+
conn = get_cloud_connection()
|
113
|
+
|
114
|
+
if all_nodes:
|
115
|
+
deploy_nodes = list(conn.baremetal.nodes(details=True))
|
116
|
+
else:
|
117
|
+
deploy_nodes = [
|
118
|
+
conn.baremetal.find_node(name, ignore_missing=True, details=True)
|
119
|
+
for name in names
|
120
|
+
]
|
121
|
+
|
122
|
+
for node_idx, node in enumerate(deploy_nodes):
|
123
|
+
if not node:
|
124
|
+
logger.warning(f"Could not find node {names[node_idx]}")
|
125
|
+
continue
|
126
|
+
|
127
|
+
if node.provision_state in ["available", "deploy failed"]:
|
128
|
+
provision_state = "active"
|
129
|
+
elif (
|
130
|
+
node.provision_state == "error"
|
131
|
+
or node.provision_state == "active"
|
132
|
+
and rebuild
|
133
|
+
):
|
134
|
+
provision_state = "rebuild"
|
135
|
+
else:
|
136
|
+
logger.warning(
|
137
|
+
f"Node {node.name} ({node.id}) not in supported provision state"
|
138
|
+
)
|
139
|
+
continue
|
140
|
+
|
141
|
+
try:
|
142
|
+
conn.baremetal.validate_node(
|
143
|
+
node.id, required=("boot", "deploy", "power")
|
144
|
+
)
|
145
|
+
except openstack.exceptions.ValidationException:
|
146
|
+
logger.warning(f"Node {node.name} ({node.id}) could not be validated")
|
147
|
+
continue
|
148
|
+
try:
|
149
|
+
config_drive = {"meta_data": {}}
|
150
|
+
if (
|
151
|
+
"netplan_parameters" in node.extra
|
152
|
+
and node.extra["netplan_parameters"]
|
153
|
+
):
|
154
|
+
config_drive["meta_data"].update(
|
155
|
+
{
|
156
|
+
"netplan_parameters": json.loads(
|
157
|
+
node.extra["netplan_parameters"]
|
158
|
+
)
|
159
|
+
}
|
160
|
+
)
|
161
|
+
if "frr_parameters" in node.extra and node.extra["frr_parameters"]:
|
162
|
+
config_drive["meta_data"].update(
|
163
|
+
{"frr_parameters": json.loads(node.extra["frr_parameters"])}
|
164
|
+
)
|
165
|
+
conn.baremetal.set_node_provision_state(
|
166
|
+
node.id, provision_state, config_drive=config_drive
|
167
|
+
)
|
168
|
+
except Exception as exc:
|
169
|
+
logger.warning(
|
170
|
+
f"Node {node.name} ({node.id}) could not be moved to active state: {exc}"
|
171
|
+
)
|
172
|
+
continue
|
173
|
+
|
174
|
+
|
175
|
+
class BaremetalUndeploy(Command):
|
176
|
+
def get_parser(self, prog_name):
|
177
|
+
parser = super(BaremetalUndeploy, self).get_parser(prog_name)
|
178
|
+
|
179
|
+
parser_exc_group = parser.add_mutually_exclusive_group(required=True)
|
180
|
+
parser_exc_group.add_argument(
|
181
|
+
"--all",
|
182
|
+
default=False,
|
183
|
+
help="Undeploy all baremetal nodes",
|
184
|
+
action="store_true",
|
185
|
+
)
|
186
|
+
parser_exc_group.add_argument(
|
187
|
+
"--name",
|
188
|
+
default=[],
|
189
|
+
help="Undeploy given baremetal node. May be specified multiple times",
|
190
|
+
action="append",
|
191
|
+
)
|
192
|
+
parser.add_argument(
|
193
|
+
"--yes-i-really-really-mean-it",
|
194
|
+
default=False,
|
195
|
+
help="Specify this to actually undeploy all nodes",
|
196
|
+
action="store_true",
|
197
|
+
)
|
198
|
+
return parser
|
199
|
+
|
200
|
+
def take_action(self, parsed_args):
|
201
|
+
all_nodes = parsed_args.all
|
202
|
+
names = parsed_args.name
|
203
|
+
yes_i_really_really_mean_it = parsed_args.yes_i_really_really_mean_it
|
204
|
+
|
205
|
+
if all_nodes and not yes_i_really_really_mean_it:
|
206
|
+
logger.error(
|
207
|
+
"Please confirm that you wish to undeploy all nodes by specifying '--yes-i-really-really-mean-it'"
|
208
|
+
)
|
209
|
+
return
|
210
|
+
|
211
|
+
conn = get_cloud_connection()
|
212
|
+
|
213
|
+
if all_nodes:
|
214
|
+
deploy_nodes = list(conn.baremetal.nodes())
|
215
|
+
else:
|
216
|
+
deploy_nodes = [
|
217
|
+
conn.baremetal.find_node(name, ignore_missing=True, details=False)
|
218
|
+
for name in names
|
219
|
+
]
|
220
|
+
|
221
|
+
for node_idx, node in enumerate(deploy_nodes):
|
222
|
+
if not node:
|
223
|
+
logger.warning(f"Could not find node {names[node_idx]}")
|
224
|
+
continue
|
225
|
+
|
226
|
+
if node.provision_state in ["active", "deploy failed", "error"]:
|
227
|
+
try:
|
228
|
+
conn.baremetal.set_node_provision_state(node.id, "undeploy")
|
229
|
+
except Exception as exc:
|
230
|
+
logger.warning(
|
231
|
+
f"Node {node.name} ({node.id}) could not be moved to available state: {exc}"
|
232
|
+
)
|
233
|
+
continue
|
234
|
+
else:
|
235
|
+
logger.warning(
|
236
|
+
f"Node {node.name} ({node.id}) not in supported provision state"
|
237
|
+
)
|
osism/commands/netbox.py
CHANGED
osism/tasks/conductor.py
CHANGED
@@ -10,6 +10,7 @@ import jinja2
|
|
10
10
|
from loguru import logger
|
11
11
|
from pottery import Redlock
|
12
12
|
import yaml
|
13
|
+
import json
|
13
14
|
|
14
15
|
from osism import settings
|
15
16
|
from osism import utils
|
@@ -305,6 +306,27 @@ def sync_ironic(self, force_update=False):
|
|
305
306
|
)
|
306
307
|
)
|
307
308
|
node_attributes.update({"resource_class": device.name})
|
309
|
+
# NOTE: Write metadata used for provisioning into 'extra' field, so that it is available during node deploy without querying the netbox again
|
310
|
+
if "extra" not in node_attributes:
|
311
|
+
node_attributes["extra"] = {}
|
312
|
+
if (
|
313
|
+
"netplan_parameters" in device.custom_fields
|
314
|
+
and device.custom_fields["netplan_parameters"]
|
315
|
+
):
|
316
|
+
node_attributes["extra"].update(
|
317
|
+
{
|
318
|
+
"netplan_parameters": json.dumps(
|
319
|
+
device.custom_fields["netplan_parameters"]
|
320
|
+
)
|
321
|
+
}
|
322
|
+
)
|
323
|
+
if (
|
324
|
+
"frr_parameters" in device.custom_fields
|
325
|
+
and device.custom_fields["frr_parameters"]
|
326
|
+
):
|
327
|
+
node_attributes["extra"].update(
|
328
|
+
{"frr_parameters": json.dumps(device.custom_fields["frr_parameters"])}
|
329
|
+
)
|
308
330
|
ports_attributes = [
|
309
331
|
dict(address=interface.mac_address)
|
310
332
|
for interface in node_interfaces
|
osism/tasks/netbox.py
CHANGED
@@ -151,6 +151,11 @@ def get_interfaces_by_device(self, device_name):
|
|
151
151
|
return utils.nb.dcim.interfaces.filter(device=device_name)
|
152
152
|
|
153
153
|
|
154
|
+
@app.task(bind=True, name="osism.tasks.netbox.get_addresses_by_device_and_interface")
|
155
|
+
def get_addresses_by_device_and_interface(self, device_name, interface_name):
|
156
|
+
return utils.nb.dcim.addresses.filter(device=device_name, interface=interface_name)
|
157
|
+
|
158
|
+
|
154
159
|
@app.task(bind=True, name="osism.tasks.netbox.manage")
|
155
160
|
def manage(self, *arguments, publish=True, locking=False, auto_release_time=3600):
|
156
161
|
netbox_manager_env = {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: osism
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.20250530.0
|
4
4
|
Summary: OSISM manager interface
|
5
5
|
Home-page: https://github.com/osism/python-osism
|
6
6
|
Author: OSISM GmbH
|
@@ -48,7 +48,7 @@ Requires-Dist: prompt-toolkit==3.0.51
|
|
48
48
|
Requires-Dist: pynetbox==7.5.0
|
49
49
|
Requires-Dist: pytest-testinfra==10.2.2
|
50
50
|
Requires-Dist: python-dateutil==2.9.0.post0
|
51
|
-
Requires-Dist: setuptools==80.
|
51
|
+
Requires-Dist: setuptools==80.9.0
|
52
52
|
Requires-Dist: sqlmodel==0.0.24
|
53
53
|
Requires-Dist: sushy==5.6.0
|
54
54
|
Requires-Dist: tabulate==0.9.0
|
@@ -6,6 +6,7 @@ osism/settings.py,sha256=mkvbxVQ64ZD7Ypk-bRePHn0gZ5j6Lcu2a578eLU0gQs,1309
|
|
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=mH3-NctgevVzP_1IW92FQeiYMCPB49K5hXbxmTY2vnA,16795
|
9
|
+
osism/commands/baremetal.py,sha256=EDLorDkt3iUblIZb-Fn6fonnMewVEOyrLBTlfxuQ2tc,8043
|
9
10
|
osism/commands/compose.py,sha256=iqzG7mS9E1VWaLNN6yQowjOqiHn3BMdj-yfXb3Dc4Ok,1200
|
10
11
|
osism/commands/compute.py,sha256=cgqXWJa5wAvn-7e3FWCgX6hie_aK0yrKRkcNzjLXwDY,25799
|
11
12
|
osism/commands/configuration.py,sha256=sPe8b0dVKFRbr30xoeVdAnHbGwCwgUh0xa_Vzv5pSQQ,954
|
@@ -14,7 +15,7 @@ osism/commands/container.py,sha256=Fku2GaCM3Idq_FxExUtNqjrEM0XYjpVvXmueSVO8S_c,1
|
|
14
15
|
osism/commands/get.py,sha256=ryytjtXWmlMV0NucP5tGkMZu0nIlC4xVtjRk4iMZ06c,8967
|
15
16
|
osism/commands/log.py,sha256=2IpYuosC7FZwwLvM8HmKSU1NRNIelVVYzqjjVMCrOJk,4072
|
16
17
|
osism/commands/manage.py,sha256=WxUZEhylZj2IhydAe3BAr3S5ED6opG243skfSq5q41s,11971
|
17
|
-
osism/commands/netbox.py,sha256=
|
18
|
+
osism/commands/netbox.py,sha256=PagHlAwEyzKzhrqi4nikkD8sjnVqAzzbDt8PVkl2j9k,6651
|
18
19
|
osism/commands/noset.py,sha256=7zDFuFMyNpo7DUOKcNiYV8nodtdMOYFp5LDPcuJhlZ8,1481
|
19
20
|
osism/commands/reconciler.py,sha256=xOyPzQj66xwjdQd2ysCTHX2yBvmMVMppUDZTas6voXc,2882
|
20
21
|
osism/commands/server.py,sha256=avmoOv5rjOi-fN2A-27cPwOtiy2Q2j6UFtCh3QrfWAI,7512
|
@@ -38,18 +39,18 @@ osism/services/listener.py,sha256=eEamlQsJqCuU9K2QFmk3yM9LAJZEanVcTLtGMsNCKjs,97
|
|
38
39
|
osism/tasks/__init__.py,sha256=ZEu_KYsapTYp0etr-rLqie_NT_LndHDDpx53xITru5Y,8691
|
39
40
|
osism/tasks/ansible.py,sha256=_2zrHwynwwEv9nDnX-LbNCzcwy9dTUGo_yyutt34HyQ,1346
|
40
41
|
osism/tasks/ceph.py,sha256=eIQkah3Kj4INtOkF9kTjHbXJ3_J2lg48EWJKfHc-UYw,615
|
41
|
-
osism/tasks/conductor.py,sha256=
|
42
|
+
osism/tasks/conductor.py,sha256=okLlmuSsJpmu6zQOR_dXX0LMQI1xFBlHRm76qB6txRk,20502
|
42
43
|
osism/tasks/kolla.py,sha256=wJQpWn_01iWLkr7l7T7RNrQGfRgsgmYi4WQlTmNGvew,618
|
43
44
|
osism/tasks/kubernetes.py,sha256=VzXq_VrYU_CLm4cOruqnE3Kq2ydfO9glZ3p0bp3OYoc,625
|
44
|
-
osism/tasks/netbox.py,sha256=
|
45
|
+
osism/tasks/netbox.py,sha256=Dq2hg2yiv_dHV-zygJgy9T1ZhTSE32_a34fhfURUfTA,5912
|
45
46
|
osism/tasks/openstack.py,sha256=g15tCll5vP1pC6ysxRCTZxplsdGmXbxaCH3k1Qdv5Xg,6367
|
46
47
|
osism/tasks/reconciler.py,sha256=tnZEZZpveBCK4vHZkHE6wDcHfJAlsPcSjIVxB5ItSFM,1981
|
47
48
|
osism/utils/__init__.py,sha256=_Y4qchR5yyI_JKhBWd_jcsvDLYZjxO0c3iMA_VRQl58,4304
|
48
|
-
osism-0.
|
49
|
-
osism-0.
|
50
|
-
osism-0.
|
51
|
-
osism-0.
|
52
|
-
osism-0.
|
53
|
-
osism-0.
|
54
|
-
osism-0.
|
55
|
-
osism-0.
|
49
|
+
osism-0.20250530.0.dist-info/licenses/AUTHORS,sha256=EKFIR9F27AvoEXp1cA6FkGbjEOFt4Rcbipr5RJc7jSs,64
|
50
|
+
osism-0.20250530.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
51
|
+
osism-0.20250530.0.dist-info/METADATA,sha256=p5ZH0PS7XC8iz2s-FFeGy10nd93NDinuBAS-YXCmPu4,2903
|
52
|
+
osism-0.20250530.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
53
|
+
osism-0.20250530.0.dist-info/entry_points.txt,sha256=cTxzUWJff6JKe1Jv-iMMKayIiC26S1_Fph3k5faiQvw,3252
|
54
|
+
osism-0.20250530.0.dist-info/pbr.json,sha256=3Gdc4g_dd2jHbkHZk28onO59wzLpSeDVrRWZwOrw3Wk,47
|
55
|
+
osism-0.20250530.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
|
56
|
+
osism-0.20250530.0.dist-info/RECORD,,
|
@@ -26,6 +26,9 @@ log ansible = osism.commands.log:Ansible
|
|
26
26
|
log container = osism.commands.log:Container
|
27
27
|
log file = osism.commands.log:File
|
28
28
|
log opensearch = osism.commands.log:Opensearch
|
29
|
+
manage baremetal deploy = osism.commands.baremetal:BaremetalDeploy
|
30
|
+
manage baremetal list = osism.commands.baremetal:BaremetalList
|
31
|
+
manage baremetal undeploy = osism.commands.baremetal:BaremetalUndeploy
|
29
32
|
manage compute disable = osism.commands.compute:ComputeDisable
|
30
33
|
manage compute enable = osism.commands.compute:ComputeEnable
|
31
34
|
manage compute evacuate = osism.commands.compute:ComputeEvacuate
|
@@ -0,0 +1 @@
|
|
1
|
+
{"git_version": "424407f", "is_release": false}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"git_version": "3c67308", "is_release": false}
|
File without changes
|
File without changes
|
File without changes
|