osism 0.20250605.0__py3-none-any.whl → 0.20250621.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 +71 -10
- osism/commands/manage.py +25 -1
- osism/commands/netbox.py +22 -5
- osism/commands/reconciler.py +6 -28
- osism/commands/sync.py +48 -1
- osism/commands/validate.py +7 -30
- osism/commands/wait.py +8 -31
- osism/services/listener.py +1 -1
- osism/settings.py +13 -2
- osism/tasks/__init__.py +8 -40
- osism/tasks/conductor/__init__.py +8 -1
- osism/tasks/conductor/ironic.py +90 -66
- osism/tasks/conductor/netbox.py +267 -6
- osism/tasks/conductor/sonic/__init__.py +26 -0
- osism/tasks/conductor/sonic/bgp.py +87 -0
- osism/tasks/conductor/sonic/cache.py +114 -0
- osism/tasks/conductor/sonic/config_generator.py +908 -0
- osism/tasks/conductor/sonic/connections.py +389 -0
- osism/tasks/conductor/sonic/constants.py +79 -0
- osism/tasks/conductor/sonic/device.py +82 -0
- osism/tasks/conductor/sonic/exporter.py +226 -0
- osism/tasks/conductor/sonic/interface.py +789 -0
- osism/tasks/conductor/sonic/sync.py +190 -0
- osism/tasks/conductor.py +2 -0
- osism/tasks/netbox.py +6 -4
- osism/tasks/reconciler.py +4 -5
- osism/utils/__init__.py +51 -4
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/METADATA +4 -4
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/RECORD +35 -25
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/entry_points.txt +5 -0
- osism-0.20250621.0.dist-info/pbr.json +1 -0
- osism-0.20250605.0.dist-info/pbr.json +0 -1
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/WHEEL +0 -0
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/licenses/AUTHORS +0 -0
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/licenses/LICENSE +0 -0
- {osism-0.20250605.0.dist-info → osism-0.20250621.0.dist-info}/top_level.txt +0 -0
osism/commands/baremetal.py
CHANGED
@@ -2,10 +2,14 @@
|
|
2
2
|
|
3
3
|
from cliff.command import Command
|
4
4
|
|
5
|
+
import tempfile
|
6
|
+
import os
|
5
7
|
from loguru import logger
|
6
8
|
import openstack
|
7
9
|
from tabulate import tabulate
|
8
10
|
import json
|
11
|
+
import yaml
|
12
|
+
from openstack.baremetal import configdrive as configdrive_builder
|
9
13
|
|
10
14
|
from osism.commands import get_cloud_connection
|
11
15
|
|
@@ -127,20 +131,35 @@ class BaremetalDeploy(Command):
|
|
127
131
|
if not node:
|
128
132
|
continue
|
129
133
|
|
130
|
-
if
|
134
|
+
if (
|
135
|
+
node.provision_state in ["available", "deploy failed"]
|
136
|
+
and not node["maintenance"]
|
137
|
+
):
|
131
138
|
provision_state = "active"
|
132
139
|
elif (
|
133
140
|
node.provision_state == "error"
|
134
141
|
or node.provision_state == "active"
|
142
|
+
and not node["maintenance"]
|
135
143
|
and rebuild
|
136
144
|
):
|
137
145
|
provision_state = "rebuild"
|
138
146
|
else:
|
139
147
|
logger.warning(
|
140
|
-
f"Node {node.name} ({node.id}) not in supported
|
148
|
+
f"Node {node.name} ({node.id}) not in supported state! Provision state: {node.provision_state}, maintenance mode: {node['maintenance']}"
|
141
149
|
)
|
142
150
|
continue
|
143
151
|
|
152
|
+
# NOTE: Ironic removes "instance_info" on undeploy. It was saved to "extra" during sync and needs to be refreshed here.
|
153
|
+
if (
|
154
|
+
"instance_info" in node
|
155
|
+
and not node["instance_info"]
|
156
|
+
and "instance_info" in node["extra"]
|
157
|
+
and node["extra"]["instance_info"]
|
158
|
+
):
|
159
|
+
node = conn.baremetal.update_node(
|
160
|
+
node, instance_info=json.loads(node.extra["instance_info"])
|
161
|
+
)
|
162
|
+
|
144
163
|
try:
|
145
164
|
conn.baremetal.validate_node(
|
146
165
|
node.id, required=("boot", "deploy", "power")
|
@@ -148,23 +167,60 @@ class BaremetalDeploy(Command):
|
|
148
167
|
except openstack.exceptions.ValidationException:
|
149
168
|
logger.warning(f"Node {node.name} ({node.id}) could not be validated")
|
150
169
|
continue
|
170
|
+
# NOTE: Prepare osism config drive
|
151
171
|
try:
|
152
|
-
|
172
|
+
playbook = []
|
173
|
+
play = {
|
174
|
+
"name": "Run bootstrap - part 2",
|
175
|
+
"hosts": "localhost",
|
176
|
+
"connection": "local",
|
177
|
+
"gather_facts": True,
|
178
|
+
"vars": {},
|
179
|
+
"roles": [
|
180
|
+
"osism.commons.hostname",
|
181
|
+
"osism.commons.hosts",
|
182
|
+
],
|
183
|
+
}
|
184
|
+
play["vars"].update(
|
185
|
+
{"hostname_name": node.name, "hosts_type": "template"}
|
186
|
+
)
|
153
187
|
if (
|
154
188
|
"netplan_parameters" in node.extra
|
155
189
|
and node.extra["netplan_parameters"]
|
156
190
|
):
|
157
|
-
|
191
|
+
play["vars"].update(
|
158
192
|
{
|
159
|
-
"
|
160
|
-
node.extra["netplan_parameters"]
|
161
|
-
)
|
193
|
+
"network_allow_service_restart": True,
|
162
194
|
}
|
163
195
|
)
|
196
|
+
play["vars"].update(json.loads(node.extra["netplan_parameters"]))
|
197
|
+
play["roles"].append("osism.commons.network")
|
164
198
|
if "frr_parameters" in node.extra and node.extra["frr_parameters"]:
|
165
|
-
|
166
|
-
{
|
199
|
+
play["vars"].update(
|
200
|
+
{
|
201
|
+
"frr_dummy_interface": "loopback0",
|
202
|
+
}
|
167
203
|
)
|
204
|
+
play["vars"].update(json.loads(node.extra["frr_parameters"]))
|
205
|
+
play["roles"].append("osism.services.frr")
|
206
|
+
playbook.append(play)
|
207
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
208
|
+
with open(os.path.join(tmp_dir, "playbook.yml"), "w") as file:
|
209
|
+
yaml.dump(
|
210
|
+
playbook,
|
211
|
+
file,
|
212
|
+
default_flow_style=False,
|
213
|
+
explicit_start=True,
|
214
|
+
indent=2,
|
215
|
+
sort_keys=False,
|
216
|
+
)
|
217
|
+
config_drive = configdrive_builder.pack(tmp_dir)
|
218
|
+
except Exception as exc:
|
219
|
+
logger.warning(
|
220
|
+
f"Failed to build config drive for {node.name} ({node.id}): {exc}"
|
221
|
+
)
|
222
|
+
continue
|
223
|
+
try:
|
168
224
|
conn.baremetal.set_node_provision_state(
|
169
225
|
node.id, provision_state, config_drive=config_drive
|
170
226
|
)
|
@@ -231,12 +287,17 @@ class BaremetalUndeploy(Command):
|
|
231
287
|
|
232
288
|
if node.provision_state in ["active", "deploy failed", "error"]:
|
233
289
|
try:
|
234
|
-
conn.baremetal.set_node_provision_state(node.id, "undeploy")
|
290
|
+
node = conn.baremetal.set_node_provision_state(node.id, "undeploy")
|
235
291
|
except Exception as exc:
|
236
292
|
logger.warning(
|
237
293
|
f"Node {node.name} ({node.id}) could not be moved to available state: {exc}"
|
238
294
|
)
|
239
295
|
continue
|
296
|
+
# NOTE: Ironic removes "instance_info" on undeploy. It was saved to "extra" during sync and needs to be refreshed here.
|
297
|
+
if "instance_info" in node["extra"]:
|
298
|
+
node = conn.baremetal.update_node(
|
299
|
+
node, instance_info=json.loads(node.extra["instance_info"])
|
300
|
+
)
|
240
301
|
else:
|
241
302
|
logger.warning(
|
242
303
|
f"Node {node.name} ({node.id}) not in supported provision state"
|
osism/commands/manage.py
CHANGED
@@ -10,7 +10,7 @@ from loguru import logger
|
|
10
10
|
import requests
|
11
11
|
|
12
12
|
from osism.data import TEMPLATE_IMAGE_CLUSTERAPI, TEMPLATE_IMAGE_OCTAVIA
|
13
|
-
from osism.tasks import openstack, handle_task
|
13
|
+
from osism.tasks import openstack, ansible, handle_task
|
14
14
|
|
15
15
|
SUPPORTED_CLUSTERAPI_K8S_IMAGES = ["1.31", "1.32", "1.33"]
|
16
16
|
|
@@ -360,3 +360,27 @@ class Flavors(Command):
|
|
360
360
|
)
|
361
361
|
|
362
362
|
return handle_task(task, wait, format="script", timeout=3600)
|
363
|
+
|
364
|
+
|
365
|
+
class Dnsmasq(Command):
|
366
|
+
def get_parser(self, prog_name):
|
367
|
+
parser = super(Dnsmasq, self).get_parser(prog_name)
|
368
|
+
parser.add_argument(
|
369
|
+
"--no-wait",
|
370
|
+
default=False,
|
371
|
+
help="Do not wait until dnsmasq has been applied",
|
372
|
+
action="store_true",
|
373
|
+
)
|
374
|
+
return parser
|
375
|
+
|
376
|
+
def take_action(self, parsed_args):
|
377
|
+
wait = not parsed_args.no_wait
|
378
|
+
|
379
|
+
task_signature = ansible.run.si("infrastructure", "dnsmasq", [])
|
380
|
+
task = task_signature.apply_async()
|
381
|
+
if wait:
|
382
|
+
logger.info(
|
383
|
+
f"It takes a moment until task {task.task_id} (dnsmasq) has been started and output is visible here."
|
384
|
+
)
|
385
|
+
|
386
|
+
return handle_task(task, wait, format="log", timeout=300)
|
osism/commands/netbox.py
CHANGED
@@ -8,6 +8,7 @@ from loguru import logger
|
|
8
8
|
import yaml
|
9
9
|
|
10
10
|
from osism.tasks import conductor, netbox, handle_task
|
11
|
+
from osism import utils
|
11
12
|
|
12
13
|
|
13
14
|
class Ironic(Command):
|
@@ -18,6 +19,12 @@ class Ironic(Command):
|
|
18
19
|
help="Do not wait until the sync has been completed",
|
19
20
|
action="store_true",
|
20
21
|
)
|
22
|
+
parser.add_argument(
|
23
|
+
"--task-timeout",
|
24
|
+
default=os.environ.get("OSISM_TASK_TIMEOUT", 300),
|
25
|
+
type=int,
|
26
|
+
help="Timeout for a scheduled task that has not been executed yet",
|
27
|
+
)
|
21
28
|
parser.add_argument(
|
22
29
|
"--force-update",
|
23
30
|
help="Force update of baremetal nodes (Used to update non-comparable items like passwords)",
|
@@ -27,13 +34,23 @@ class Ironic(Command):
|
|
27
34
|
|
28
35
|
def take_action(self, parsed_args):
|
29
36
|
wait = not parsed_args.no_wait
|
37
|
+
task_timeout = parsed_args.task_timeout
|
30
38
|
|
31
39
|
task = conductor.sync_ironic.delay(force_update=parsed_args.force_update)
|
32
40
|
if wait:
|
33
41
|
logger.info(
|
34
|
-
f"Task {task.task_id} (sync ironic) is running
|
42
|
+
f"Task {task.task_id} (sync ironic) is running in background. Output comming soon."
|
43
|
+
)
|
44
|
+
try:
|
45
|
+
return utils.fetch_task_output(task.id, timeout=task_timeout)
|
46
|
+
except TimeoutError:
|
47
|
+
logger.error(
|
48
|
+
f"Timeout while waiting for further output of task {task.task_id} (sync ironic)"
|
49
|
+
)
|
50
|
+
else:
|
51
|
+
logger.info(
|
52
|
+
f"Task {task.task_id} (sync ironic) is running in background. No more output."
|
35
53
|
)
|
36
|
-
task.wait(timeout=None, interval=0.5)
|
37
54
|
|
38
55
|
|
39
56
|
class Sync(Command):
|
@@ -63,13 +80,13 @@ class Manage(Command):
|
|
63
80
|
parser.add_argument(
|
64
81
|
"--no-wait",
|
65
82
|
default=False,
|
66
|
-
help="Do not wait until the management of the
|
83
|
+
help="Do not wait until the management of the NetBox has been completed",
|
67
84
|
action="store_true",
|
68
85
|
)
|
69
86
|
parser.add_argument(
|
70
87
|
"--no-netbox-wait",
|
71
88
|
default=False,
|
72
|
-
help="Do not wait for the
|
89
|
+
help="Do not wait for the NetBox API to be ready",
|
73
90
|
action="store_true",
|
74
91
|
)
|
75
92
|
parser.add_argument(
|
@@ -195,7 +212,7 @@ class Console(Command):
|
|
195
212
|
url = os.environ.get("NETBOX_API", None)
|
196
213
|
|
197
214
|
if not token or not url:
|
198
|
-
logger.error("
|
215
|
+
logger.error("NetBox integration not configured.")
|
199
216
|
return
|
200
217
|
|
201
218
|
subprocess.call(
|
osism/commands/reconciler.py
CHANGED
@@ -2,13 +2,12 @@
|
|
2
2
|
|
3
3
|
import os
|
4
4
|
import subprocess
|
5
|
-
import time
|
6
5
|
|
7
6
|
from cliff.command import Command
|
8
7
|
from loguru import logger
|
9
8
|
|
10
9
|
from osism.tasks import reconciler
|
11
|
-
from osism
|
10
|
+
from osism import utils
|
12
11
|
|
13
12
|
|
14
13
|
class Run(Command):
|
@@ -50,33 +49,12 @@ class Sync(Command):
|
|
50
49
|
logger.info(
|
51
50
|
f"Task {t.task_id} (sync inventory) is running in background. Output coming soon."
|
52
51
|
)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
{str(t.task_id): last_id}, count=1, block=(300 * 1000)
|
52
|
+
try:
|
53
|
+
return utils.fetch_task_output(t.id, timeout=task_timeout)
|
54
|
+
except TimeoutError:
|
55
|
+
logger.error(
|
56
|
+
f"Timeout while waiting for further output of task {t.task_id} (sync inventory)"
|
59
57
|
)
|
60
|
-
if data:
|
61
|
-
stoptime = time.time() + task_timeout
|
62
|
-
messages = data[0]
|
63
|
-
for message_id, message in messages[1]:
|
64
|
-
last_id = message_id.decode()
|
65
|
-
message_type = message[b"type"].decode()
|
66
|
-
message_content = message[b"content"].decode()
|
67
|
-
|
68
|
-
logger.debug(
|
69
|
-
f"Processing message {last_id} of type {message_type}"
|
70
|
-
)
|
71
|
-
redis.xdel(str(t.task_id), last_id)
|
72
|
-
|
73
|
-
if message_type == "stdout":
|
74
|
-
print(message_content, end="")
|
75
|
-
elif message_type == "rc":
|
76
|
-
rc = int(message_content)
|
77
|
-
elif message_type == "action" and message_content == "quit":
|
78
|
-
redis.close()
|
79
|
-
return rc
|
80
58
|
else:
|
81
59
|
logger.info(
|
82
60
|
f"Task {t.task_id} (sync inventory) is running in background. No more output."
|
osism/commands/sync.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
2
2
|
|
3
3
|
from cliff.command import Command
|
4
|
+
from loguru import logger
|
4
5
|
|
5
|
-
from osism.tasks import ansible, handle_task
|
6
|
+
from osism.tasks import ansible, conductor, handle_task
|
6
7
|
|
7
8
|
|
8
9
|
class Facts(Command):
|
@@ -17,3 +18,49 @@ class Facts(Command):
|
|
17
18
|
)
|
18
19
|
rc = handle_task(t)
|
19
20
|
return rc
|
21
|
+
|
22
|
+
|
23
|
+
class Sonic(Command):
|
24
|
+
def get_parser(self, prog_name):
|
25
|
+
parser = super(Sonic, self).get_parser(prog_name)
|
26
|
+
parser.add_argument(
|
27
|
+
"device",
|
28
|
+
nargs="?",
|
29
|
+
help="Optional device name to sync configuration for a specific device",
|
30
|
+
)
|
31
|
+
parser.add_argument(
|
32
|
+
"--no-wait",
|
33
|
+
default=False,
|
34
|
+
help="Do not wait until the sync has been completed",
|
35
|
+
action="store_true",
|
36
|
+
)
|
37
|
+
parser.add_argument(
|
38
|
+
"--diff",
|
39
|
+
default=True,
|
40
|
+
help="Show configuration diff when changes are detected (default: True)",
|
41
|
+
action="store_true",
|
42
|
+
)
|
43
|
+
parser.add_argument(
|
44
|
+
"--no-diff",
|
45
|
+
dest="diff",
|
46
|
+
help="Do not show configuration diff",
|
47
|
+
action="store_false",
|
48
|
+
)
|
49
|
+
return parser
|
50
|
+
|
51
|
+
def take_action(self, parsed_args):
|
52
|
+
wait = not parsed_args.no_wait
|
53
|
+
device_name = parsed_args.device
|
54
|
+
show_diff = parsed_args.diff
|
55
|
+
|
56
|
+
task = conductor.sync_sonic.delay(device_name, show_diff)
|
57
|
+
|
58
|
+
if device_name:
|
59
|
+
logger.info(
|
60
|
+
f"Task {task.task_id} (sync sonic for device {device_name}) started"
|
61
|
+
)
|
62
|
+
else:
|
63
|
+
logger.info(f"Task {task.task_id} (sync sonic) started")
|
64
|
+
|
65
|
+
rc = handle_task(task, wait=wait)
|
66
|
+
return rc
|
osism/commands/validate.py
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
2
2
|
|
3
3
|
import argparse
|
4
|
-
import time
|
5
4
|
|
6
5
|
from cliff.command import Command
|
7
6
|
from loguru import logger
|
8
7
|
|
9
8
|
from osism.core.enums import VALIDATE_PLAYBOOKS
|
10
9
|
from osism.tasks import ansible, ceph, kolla
|
11
|
-
from osism
|
10
|
+
from osism import utils
|
12
11
|
|
13
12
|
|
14
13
|
class Run(Command):
|
@@ -53,35 +52,13 @@ class Run(Command):
|
|
53
52
|
return parser
|
54
53
|
|
55
54
|
def _handle_task(self, t, wait, format, timeout, playbook):
|
56
|
-
rc = 0
|
57
55
|
if wait:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
{
|
56
|
+
try:
|
57
|
+
return utils.fetch_task_output(t.id, timeout=timeout)
|
58
|
+
except TimeoutError:
|
59
|
+
logger.error(
|
60
|
+
f"Timeout while waiting for further output of task {t.task_id} (sync inventory)"
|
63
61
|
)
|
64
|
-
if data:
|
65
|
-
stoptime = time.time() + timeout
|
66
|
-
messages = data[0]
|
67
|
-
for message_id, message in messages[1]:
|
68
|
-
last_id = message_id.decode()
|
69
|
-
message_type = message[b"type"].decode()
|
70
|
-
message_content = message[b"content"].decode()
|
71
|
-
|
72
|
-
logger.debug(
|
73
|
-
f"Processing message {last_id} of type {message_type}"
|
74
|
-
)
|
75
|
-
redis.xdel(str(t.task_id), message_id)
|
76
|
-
|
77
|
-
if message_type == "stdout":
|
78
|
-
print(message_content, end="")
|
79
|
-
elif message_type == "rc":
|
80
|
-
rc = int(message_content)
|
81
|
-
elif message_type == "action" and message_content == "quit":
|
82
|
-
redis.close()
|
83
|
-
return rc
|
84
|
-
|
85
62
|
else:
|
86
63
|
if format == "log":
|
87
64
|
logger.info(
|
@@ -90,7 +67,7 @@ class Run(Command):
|
|
90
67
|
elif format == "script":
|
91
68
|
print(f"{t.task_id}")
|
92
69
|
|
93
|
-
return
|
70
|
+
return 0
|
94
71
|
|
95
72
|
def take_action(self, parsed_args):
|
96
73
|
arguments = parsed_args.arguments
|
osism/commands/wait.py
CHANGED
@@ -119,38 +119,15 @@ class Run(Command):
|
|
119
119
|
|
120
120
|
if live:
|
121
121
|
utils.redis.ping()
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
{str(task_id): last_id}, count=1, block=1000
|
122
|
+
try:
|
123
|
+
rc = utils.fetch_task_output(task_id)
|
124
|
+
except TimeoutError:
|
125
|
+
logger.error(
|
126
|
+
f"Timeout while waiting for further output of task {task_id}"
|
128
127
|
)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
last_id = message_id.decode()
|
133
|
-
message_type = message[b"type"].decode()
|
134
|
-
message_content = message[b"content"].decode()
|
135
|
-
|
136
|
-
logger.debug(
|
137
|
-
f"Processing message {last_id} of type {message_type}"
|
138
|
-
)
|
139
|
-
utils.redis.xdel(str(task_id), last_id)
|
140
|
-
|
141
|
-
if message_type == "stdout":
|
142
|
-
print(message_content, end="")
|
143
|
-
elif message_type == "rc":
|
144
|
-
rc = int(message_content)
|
145
|
-
elif (
|
146
|
-
message_type == "action"
|
147
|
-
and message_content == "quit"
|
148
|
-
):
|
149
|
-
utils.redis.close()
|
150
|
-
if len(task_ids) == 1:
|
151
|
-
return rc
|
152
|
-
else:
|
153
|
-
while_True = False
|
128
|
+
|
129
|
+
if len(task_ids) == 1:
|
130
|
+
return rc
|
154
131
|
else:
|
155
132
|
tmp_task_ids.insert(0, task_id)
|
156
133
|
|
osism/services/listener.py
CHANGED
@@ -93,7 +93,7 @@ class BaremetalEvents:
|
|
93
93
|
netbox.set_maintenance.delay(name, state=object_data["maintenance"])
|
94
94
|
|
95
95
|
def node_provision_set_success(self, payload: dict[Any, Any]) -> None:
|
96
|
-
# A provision status was successfully set, update it in the
|
96
|
+
# A provision status was successfully set, update it in the NetBox
|
97
97
|
object_data = self.get_object_data(payload)
|
98
98
|
name = object_data["name"]
|
99
99
|
logger.info(
|
osism/settings.py
CHANGED
@@ -35,11 +35,22 @@ INVENTORY_RECONCILER_SCHEDULE = float(
|
|
35
35
|
|
36
36
|
OSISM_API_URL = os.getenv("OSISM_API_URL", None)
|
37
37
|
|
38
|
-
|
39
|
-
"
|
38
|
+
NETBOX_FILTER_CONDUCTOR_IRONIC = os.getenv(
|
39
|
+
"NETBOX_FILTER_CONDUCTOR_IRONIC",
|
40
40
|
"[{'state': 'active', 'tag': ['managed-by-ironic']}]",
|
41
41
|
)
|
42
42
|
|
43
|
+
NETBOX_FILTER_CONDUCTOR_SONIC = os.getenv(
|
44
|
+
"NETBOX_FILTER_CONDUCTOR_SONIC",
|
45
|
+
"[{'state': 'active', 'tag': ['managed-by-metalbox']}]",
|
46
|
+
)
|
47
|
+
|
48
|
+
# SONiC export configuration
|
49
|
+
SONIC_EXPORT_DIR = os.getenv("SONIC_EXPORT_DIR", "/etc/sonic/export")
|
50
|
+
SONIC_EXPORT_PREFIX = os.getenv("SONIC_EXPORT_PREFIX", "osism_")
|
51
|
+
SONIC_EXPORT_SUFFIX = os.getenv("SONIC_EXPORT_SUFFIX", "_config_db.json")
|
52
|
+
SONIC_EXPORT_IDENTIFIER = os.getenv("SONIC_EXPORT_IDENTIFIER", "serial-number")
|
53
|
+
|
43
54
|
NETBOX_SECONDARIES = (
|
44
55
|
os.getenv("NETBOX_SECONDARIES", read_secret("NETBOX_SECONDARIES")) or "[]"
|
45
56
|
)
|
osism/tasks/__init__.py
CHANGED
@@ -4,7 +4,6 @@ import logging
|
|
4
4
|
import os
|
5
5
|
import re
|
6
6
|
import subprocess
|
7
|
-
import time
|
8
7
|
|
9
8
|
from loguru import logger
|
10
9
|
from pottery import Redlock
|
@@ -163,14 +162,13 @@ def run_ansible_in_environment(
|
|
163
162
|
while p.poll() is None:
|
164
163
|
line = p.stdout.readline().decode("utf-8")
|
165
164
|
if publish:
|
166
|
-
utils.
|
165
|
+
utils.push_task_output(request_id, line)
|
167
166
|
result += line
|
168
167
|
|
169
168
|
rc = p.wait(timeout=60)
|
170
169
|
|
171
170
|
if publish:
|
172
|
-
utils.
|
173
|
-
utils.redis.xadd(request_id, {"type": "action", "content": "quit"})
|
171
|
+
utils.finish_task_output(request_id, rc=rc)
|
174
172
|
|
175
173
|
if locking:
|
176
174
|
lock.release()
|
@@ -212,14 +210,13 @@ def run_command(
|
|
212
210
|
while p.poll() is None:
|
213
211
|
line = p.stdout.readline().decode("utf-8")
|
214
212
|
if publish:
|
215
|
-
utils.
|
213
|
+
utils.push_task_output(request_id, line)
|
216
214
|
result += line
|
217
215
|
|
218
216
|
rc = p.wait(timeout=60)
|
219
217
|
|
220
218
|
if publish:
|
221
|
-
utils.
|
222
|
-
utils.redis.xadd(request_id, {"type": "action", "content": "quit"})
|
219
|
+
utils.finish_task_output(request_id, rc=rc)
|
223
220
|
|
224
221
|
if locking:
|
225
222
|
lock.release()
|
@@ -228,39 +225,10 @@ def run_command(
|
|
228
225
|
|
229
226
|
|
230
227
|
def handle_task(t, wait=True, format="log", timeout=3600):
|
231
|
-
rc = 0
|
232
228
|
if wait:
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
data = utils.redis.xread(
|
237
|
-
{str(t.task_id): last_id}, count=1, block=(timeout * 1000)
|
238
|
-
)
|
239
|
-
if data:
|
240
|
-
stoptime = time.time() + timeout
|
241
|
-
messages = data[0]
|
242
|
-
for message_id, message in messages[1]:
|
243
|
-
last_id = message_id.decode()
|
244
|
-
message_type = message[b"type"].decode()
|
245
|
-
message_content = message[b"content"].decode()
|
246
|
-
|
247
|
-
logger.debug(f"Processing message {last_id} of type {message_type}")
|
248
|
-
utils.redis.xdel(str(t.task_id), last_id)
|
249
|
-
|
250
|
-
if message_type == "stdout":
|
251
|
-
print(message_content, end="", flush=True)
|
252
|
-
if "PLAY RECAP" in message_content:
|
253
|
-
logger.info(
|
254
|
-
"Play has been completed. There may now be a delay until "
|
255
|
-
"all logs have been written."
|
256
|
-
)
|
257
|
-
logger.info("Please wait and do not abort execution.")
|
258
|
-
elif message_type == "rc":
|
259
|
-
rc = int(message_content)
|
260
|
-
elif message_type == "action" and message_content == "quit":
|
261
|
-
utils.redis.close()
|
262
|
-
return rc
|
263
|
-
else:
|
229
|
+
try:
|
230
|
+
return utils.fetch_task_output(t.id, timeout=timeout)
|
231
|
+
except TimeoutError:
|
264
232
|
logger.info(
|
265
233
|
f"There has been no output from the task {t.task_id} for {timeout} second(s)."
|
266
234
|
)
|
@@ -284,4 +252,4 @@ def handle_task(t, wait=True, format="log", timeout=3600):
|
|
284
252
|
elif format == "script":
|
285
253
|
print(f"{t.task_id}")
|
286
254
|
|
287
|
-
return
|
255
|
+
return 0
|
@@ -8,6 +8,7 @@ from loguru import logger
|
|
8
8
|
from osism.tasks import Config
|
9
9
|
from osism.tasks.conductor.config import get_configuration
|
10
10
|
from osism.tasks.conductor.ironic import sync_ironic as _sync_ironic
|
11
|
+
from osism.tasks.conductor.sonic import sync_sonic as _sync_sonic
|
11
12
|
|
12
13
|
|
13
14
|
# App configuration
|
@@ -43,7 +44,12 @@ def sync_netbox(self, force_update=False):
|
|
43
44
|
|
44
45
|
@app.task(bind=True, name="osism.tasks.conductor.sync_ironic")
|
45
46
|
def sync_ironic(self, force_update=False):
|
46
|
-
_sync_ironic(get_ironic_parameters, force_update)
|
47
|
+
_sync_ironic(self.request.id, get_ironic_parameters, force_update)
|
48
|
+
|
49
|
+
|
50
|
+
@app.task(bind=True, name="osism.tasks.conductor.sync_sonic")
|
51
|
+
def sync_sonic(self, device_name=None, show_diff=True):
|
52
|
+
return _sync_sonic(device_name, self.request.id, show_diff)
|
47
53
|
|
48
54
|
|
49
55
|
__all__ = [
|
@@ -51,4 +57,5 @@ __all__ = [
|
|
51
57
|
"get_ironic_parameters",
|
52
58
|
"sync_netbox",
|
53
59
|
"sync_ironic",
|
60
|
+
"sync_sonic",
|
54
61
|
]
|