osism 0.20250312.0__py3-none-any.whl → 0.20250314.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/actions/manage_device.py +28 -887
- osism/commands/netbox.py +38 -327
- osism/services/listener.py +0 -40
- osism/settings.py +0 -3
- osism/tasks/__init__.py +44 -14
- osism/tasks/netbox.py +22 -98
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/METADATA +4 -4
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/RECORD +14 -18
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/entry_points.txt +1 -9
- osism-0.20250314.0.dist-info/pbr.json +1 -0
- osism/actions/check_configuration.py +0 -49
- osism/actions/deploy_configuration.py +0 -92
- osism/actions/diff_configuration.py +0 -59
- osism/actions/generate_configuration.py +0 -137
- osism-0.20250312.0.dist-info/pbr.json +0 -1
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/AUTHORS +0 -0
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/LICENSE +0 -0
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/WHEEL +0 -0
- {osism-0.20250312.0.dist-info → osism-0.20250314.0.dist-info}/top_level.txt +0 -0
osism/commands/netbox.py
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
2
2
|
|
3
3
|
import argparse
|
4
|
-
from glob import glob
|
5
|
-
from os.path import basename
|
6
4
|
|
7
5
|
from cliff.command import Command
|
8
6
|
from loguru import logger
|
9
7
|
from redis import Redis
|
10
|
-
from tabulate import tabulate
|
11
8
|
|
12
9
|
from osism import settings
|
13
|
-
from osism.tasks import conductor, netbox, reconciler,
|
10
|
+
from osism.tasks import conductor, netbox, reconciler, openstack, handle_task
|
14
11
|
|
15
12
|
|
16
13
|
redis = Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB)
|
@@ -77,368 +74,82 @@ class Sync(Command):
|
|
77
74
|
task.wait(timeout=None, interval=0.5)
|
78
75
|
|
79
76
|
|
80
|
-
class Init(Command):
|
81
|
-
def get_parser(self, prog_name):
|
82
|
-
parser = super(Init, self).get_parser(prog_name)
|
83
|
-
parser.add_argument(
|
84
|
-
"arguments", nargs=argparse.REMAINDER, help="Other arguments for Ansible"
|
85
|
-
)
|
86
|
-
parser.add_argument(
|
87
|
-
"--no-wait",
|
88
|
-
default=False,
|
89
|
-
help="Do not wait until the role has been applied",
|
90
|
-
action="store_true",
|
91
|
-
)
|
92
|
-
parser.add_argument(
|
93
|
-
"--format",
|
94
|
-
default="log",
|
95
|
-
help="Output type",
|
96
|
-
const="log",
|
97
|
-
nargs="?",
|
98
|
-
choices=["script", "log"],
|
99
|
-
),
|
100
|
-
return parser
|
101
|
-
|
102
|
-
def take_action(self, parsed_args):
|
103
|
-
arguments = parsed_args.arguments
|
104
|
-
format = parsed_args.format
|
105
|
-
wait = not parsed_args.no_wait
|
106
|
-
|
107
|
-
task = ansible.run.delay("netbox-local", "init", arguments)
|
108
|
-
rc = 0
|
109
|
-
|
110
|
-
if wait:
|
111
|
-
logger.info(f"Task {task.task_id} was prepared for execution.")
|
112
|
-
logger.info(
|
113
|
-
f"It takes a moment until task {task.task_id} has been started and output is visible here."
|
114
|
-
)
|
115
|
-
rc = handle_task(task, wait, format, 300)
|
116
|
-
else:
|
117
|
-
logger.info(
|
118
|
-
f"Task {task.task_id} is running in background. No more output. Check ARA for logs."
|
119
|
-
)
|
120
|
-
|
121
|
-
return rc
|
122
|
-
|
123
|
-
|
124
|
-
class Import(Command):
|
125
|
-
def get_parser(self, prog_name):
|
126
|
-
parser = super(Import, self).get_parser(prog_name)
|
127
|
-
parser.add_argument(
|
128
|
-
"--vendors",
|
129
|
-
help="Vendors from which all available device types are to be imported",
|
130
|
-
required=False,
|
131
|
-
)
|
132
|
-
parser.add_argument(
|
133
|
-
"--library",
|
134
|
-
default=False,
|
135
|
-
help="Do import device types from the device type library",
|
136
|
-
action="store_true",
|
137
|
-
)
|
138
|
-
parser.add_argument(
|
139
|
-
"--no-wait",
|
140
|
-
default=False,
|
141
|
-
help="Do not wait until the role has been applied",
|
142
|
-
action="store_true",
|
143
|
-
)
|
144
|
-
return parser
|
145
|
-
|
146
|
-
def take_action(self, parsed_args):
|
147
|
-
vendors = parsed_args.vendors
|
148
|
-
wait = not parsed_args.no_wait
|
149
|
-
|
150
|
-
task = netbox.import_device_types.delay(vendors, parsed_args.library)
|
151
|
-
|
152
|
-
if wait:
|
153
|
-
logger.info(f"Task {task.task_id} is running. Wait. No more output.")
|
154
|
-
task.wait(timeout=None, interval=0.5)
|
155
|
-
|
156
|
-
|
157
77
|
class Manage(Command):
|
158
78
|
def get_parser(self, prog_name):
|
159
79
|
parser = super(Manage, self).get_parser(prog_name)
|
160
|
-
parser.add_argument(
|
161
|
-
"--type",
|
162
|
-
type=str,
|
163
|
-
help="Type of the resource to manage",
|
164
|
-
required=False,
|
165
|
-
default="rack",
|
166
|
-
)
|
167
|
-
parser.add_argument(
|
168
|
-
"name", nargs="?", type=str, help="Name of the resource to manage"
|
169
|
-
)
|
170
|
-
parser.add_argument(
|
171
|
-
"arguments", nargs=argparse.REMAINDER, help="Other arguments for Ansible"
|
172
|
-
)
|
173
80
|
parser.add_argument(
|
174
81
|
"--no-wait",
|
175
82
|
default=False,
|
176
|
-
help="Do not wait until the
|
83
|
+
help="Do not wait until the management of the netbox has been completed",
|
177
84
|
action="store_true",
|
178
85
|
)
|
179
86
|
parser.add_argument(
|
180
|
-
"--
|
181
|
-
default="log",
|
182
|
-
help="Output type",
|
183
|
-
const="log",
|
184
|
-
nargs="?",
|
185
|
-
choices=["script", "log"],
|
186
|
-
),
|
187
|
-
return parser
|
188
|
-
|
189
|
-
def take_action(self, parsed_args):
|
190
|
-
arguments = parsed_args.arguments
|
191
|
-
format = parsed_args.format
|
192
|
-
name = parsed_args.name
|
193
|
-
type_of_resource = parsed_args.type
|
194
|
-
wait = not parsed_args.no_wait
|
195
|
-
|
196
|
-
rc = 0
|
197
|
-
if not name:
|
198
|
-
logger.info("No name of an object to be managed was given")
|
199
|
-
table = []
|
200
|
-
for playbook_type in ["rack"]:
|
201
|
-
playbooks = glob(
|
202
|
-
f"/opt/configuration/netbox/playbooks/{playbook_type}-*.yml"
|
203
|
-
)
|
204
|
-
for playbook in playbooks:
|
205
|
-
name = basename(playbook)[len(playbook_type) + 1 : -4] # noqa E203
|
206
|
-
table.append([playbook_type, name])
|
207
|
-
|
208
|
-
print(tabulate(table, headers=["Type", "Name"], tablefmt="psql"))
|
209
|
-
else:
|
210
|
-
task = ansible.run.delay(
|
211
|
-
"netbox-local", f"{type_of_resource}-{name}", arguments
|
212
|
-
)
|
213
|
-
|
214
|
-
if wait:
|
215
|
-
logger.info(f"Task {task.task_id} was prepared for execution.")
|
216
|
-
logger.info(
|
217
|
-
f"It takes a moment until task {task.task_id} has been started and output is visible here."
|
218
|
-
)
|
219
|
-
rc = handle_task(task, wait, format, 300)
|
220
|
-
else:
|
221
|
-
logger.info(
|
222
|
-
f"Task {task.task_id} is running in background. No more output. Check ARA for logs."
|
223
|
-
)
|
224
|
-
|
225
|
-
return rc
|
226
|
-
|
227
|
-
|
228
|
-
class Connect(Command):
|
229
|
-
def get_parser(self, prog_name):
|
230
|
-
parser = super(Connect, self).get_parser(prog_name)
|
231
|
-
parser.add_argument(
|
232
|
-
"name",
|
233
|
-
nargs="?",
|
234
|
-
type=str,
|
235
|
-
help="Name of the resource (a collection or a device) to connect",
|
236
|
-
)
|
237
|
-
parser.add_argument(
|
238
|
-
"--collection",
|
239
|
-
type=str,
|
240
|
-
help="Name of the collection to connect",
|
241
|
-
required=False,
|
242
|
-
),
|
243
|
-
parser.add_argument(
|
244
|
-
"--device", type=str, help="Name of the device to connect", required=False
|
245
|
-
)
|
246
|
-
parser.add_argument(
|
247
|
-
"--enforce",
|
87
|
+
"--no-netbox-wait",
|
248
88
|
default=False,
|
249
|
-
help="
|
89
|
+
help="Do not wait for the netbox API to be ready",
|
250
90
|
action="store_true",
|
251
91
|
)
|
252
92
|
parser.add_argument(
|
253
|
-
"--
|
254
|
-
)
|
255
|
-
parser.add_argument(
|
256
|
-
"--type",
|
257
|
-
type=str,
|
258
|
-
default="collection",
|
259
|
-
help="Type of the resource to connection (when not using --collection or --device)",
|
260
|
-
required=False,
|
261
|
-
)
|
262
|
-
return parser
|
263
|
-
|
264
|
-
def take_action(self, parsed_args):
|
265
|
-
name = parsed_args.name
|
266
|
-
collection = parsed_args.device
|
267
|
-
device = parsed_args.device
|
268
|
-
state = parsed_args.state
|
269
|
-
type_of_resource = parsed_args.type
|
270
|
-
enforce = parsed_args.enforce
|
271
|
-
|
272
|
-
task = None
|
273
|
-
|
274
|
-
if name:
|
275
|
-
if type_of_resource == "collection":
|
276
|
-
task = netbox.data.delay(name, "", state)
|
277
|
-
elif type_of_resource == "device":
|
278
|
-
task = netbox.data.delay("", name, state)
|
279
|
-
else:
|
280
|
-
task = netbox.data.delay(collection, device, state)
|
281
|
-
name = f"{collection}-{device}"
|
282
|
-
|
283
|
-
task.wait(timeout=None, interval=0.5)
|
284
|
-
data = task.get()
|
285
|
-
|
286
|
-
for device in data:
|
287
|
-
t = netbox.connect.delay(device, state, data, enforce)
|
288
|
-
logger.info(
|
289
|
-
f"Task {t.task_id} for device {device} is running in background"
|
290
|
-
)
|
291
|
-
logger.info(
|
292
|
-
"Tasks are running in background. No more output. Check Flower for logs."
|
293
|
-
)
|
294
|
-
|
295
|
-
|
296
|
-
class Disable(Command):
|
297
|
-
def get_parser(self, prog_name):
|
298
|
-
parser = super(Disable, self).get_parser(prog_name)
|
299
|
-
parser.add_argument(
|
300
|
-
"name",
|
301
|
-
nargs=1,
|
93
|
+
"--limit",
|
302
94
|
type=str,
|
303
|
-
|
95
|
+
default=None,
|
96
|
+
help="Limit files by prefix",
|
304
97
|
)
|
305
98
|
parser.add_argument(
|
306
|
-
"--
|
99
|
+
"--skipdtl",
|
307
100
|
default=False,
|
308
|
-
help="
|
101
|
+
help="Skip devicetype library",
|
309
102
|
action="store_true",
|
310
103
|
)
|
311
|
-
return parser
|
312
|
-
|
313
|
-
def take_action(self, parsed_args):
|
314
|
-
name = parsed_args.name[0]
|
315
|
-
wait = not parsed_args.no_wait
|
316
|
-
|
317
|
-
task = netbox.disable.delay(name)
|
318
|
-
|
319
|
-
if wait:
|
320
|
-
logger.info(f"Task {task.task_id} is running. Wait. No more output.")
|
321
|
-
task.wait(timeout=None, interval=0.5)
|
322
|
-
|
323
|
-
|
324
|
-
class Generate(Command):
|
325
|
-
def get_parser(self, prog_name):
|
326
|
-
parser = super(Generate, self).get_parser(prog_name)
|
327
104
|
parser.add_argument(
|
328
|
-
"
|
329
|
-
nargs=1,
|
330
|
-
type=str,
|
331
|
-
help="Name of the device for which the configuration is to be generated.",
|
332
|
-
)
|
333
|
-
parser.add_argument(
|
334
|
-
"--no-wait",
|
105
|
+
"--skipmtl",
|
335
106
|
default=False,
|
336
|
-
help="
|
107
|
+
help="Skip moduletype library",
|
337
108
|
action="store_true",
|
338
109
|
)
|
339
110
|
parser.add_argument(
|
340
|
-
"--
|
341
|
-
)
|
342
|
-
return parser
|
343
|
-
|
344
|
-
def take_action(self, parsed_args):
|
345
|
-
name = parsed_args.name[0]
|
346
|
-
template = parsed_args.template
|
347
|
-
wait = not parsed_args.no_wait
|
348
|
-
|
349
|
-
task = netbox.generate.delay(name, template)
|
350
|
-
|
351
|
-
if wait:
|
352
|
-
logger.info(f"Task {task.task_id} is running. Wait. No more output.")
|
353
|
-
task.wait(timeout=None, interval=0.5)
|
354
|
-
|
355
|
-
|
356
|
-
class Deploy(Command):
|
357
|
-
def get_parser(self, prog_name):
|
358
|
-
parser = super(Deploy, self).get_parser(prog_name)
|
359
|
-
parser.add_argument(
|
360
|
-
"name",
|
361
|
-
nargs=1,
|
362
|
-
type=str,
|
363
|
-
help="Name of the device for which the configuration is to be deployed",
|
364
|
-
)
|
365
|
-
parser.add_argument(
|
366
|
-
"arguments", nargs=argparse.REMAINDER, help="Other arguments for Ansible"
|
367
|
-
)
|
368
|
-
parser.add_argument(
|
369
|
-
"--no-wait",
|
111
|
+
"--skipres",
|
370
112
|
default=False,
|
371
|
-
help="
|
113
|
+
help="Skip resources",
|
372
114
|
action="store_true",
|
373
115
|
)
|
374
116
|
return parser
|
375
117
|
|
376
118
|
def take_action(self, parsed_args):
|
377
|
-
name = parsed_args.name[0]
|
378
|
-
# arguments = parsed_args.arguments
|
379
119
|
wait = not parsed_args.no_wait
|
120
|
+
arguments = []
|
380
121
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
task.wait(timeout=None, interval=0.5)
|
122
|
+
if parsed_args.no_netbox_wait:
|
123
|
+
arguments.append("--no-wait")
|
124
|
+
else:
|
125
|
+
arguments.append("--wait")
|
386
126
|
|
127
|
+
if parsed_args.limit:
|
128
|
+
arguments.append("--limit {parsed_args.limit}")
|
387
129
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
"name",
|
393
|
-
nargs=1,
|
394
|
-
type=str,
|
395
|
-
help="Name of the device for which the configuration is to be checked",
|
396
|
-
)
|
397
|
-
parser.add_argument(
|
398
|
-
"--no-wait",
|
399
|
-
default=False,
|
400
|
-
help="Do not wait until the changes have been made",
|
401
|
-
action="store_true",
|
402
|
-
)
|
403
|
-
return parser
|
130
|
+
if parsed_args.skipdtl:
|
131
|
+
arguments.append("--skipdtl")
|
132
|
+
else:
|
133
|
+
arguments.append("--no-skipdtl")
|
404
134
|
|
405
|
-
|
406
|
-
|
407
|
-
|
135
|
+
if parsed_args.skipmtl:
|
136
|
+
arguments.append("--skipmtl")
|
137
|
+
else:
|
138
|
+
arguments.append("--no-skipmtl")
|
408
139
|
|
409
|
-
|
140
|
+
if parsed_args.skipres:
|
141
|
+
arguments.append("--skipres")
|
142
|
+
else:
|
143
|
+
arguments.append("--no-skipres")
|
410
144
|
|
145
|
+
task_signature = netbox.manage.si(*arguments)
|
146
|
+
task = task_signature.apply_async()
|
411
147
|
if wait:
|
412
|
-
logger.info(
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
class Diff(Command):
|
417
|
-
def get_parser(self, prog_name):
|
418
|
-
parser = super(Diff, self).get_parser(prog_name)
|
419
|
-
parser.add_argument(
|
420
|
-
"name",
|
421
|
-
nargs=1,
|
422
|
-
type=str,
|
423
|
-
help="Name of the device for which the configuration is to be diffed",
|
424
|
-
)
|
425
|
-
parser.add_argument(
|
426
|
-
"--no-wait",
|
427
|
-
default=False,
|
428
|
-
help="Do not wait until the changes have been made",
|
429
|
-
action="store_true",
|
430
|
-
)
|
431
|
-
return parser
|
432
|
-
|
433
|
-
def take_action(self, parsed_args):
|
434
|
-
name = parsed_args.name[0]
|
435
|
-
wait = not parsed_args.no_wait
|
436
|
-
|
437
|
-
task = netbox.diff.delay(name)
|
148
|
+
logger.info(
|
149
|
+
f"It takes a moment until task {task.task_id} (netbox-manager) has been started and output is visible here."
|
150
|
+
)
|
438
151
|
|
439
|
-
|
440
|
-
logger.info(f"Task {task.task_id} is running. Wait. No more output.")
|
441
|
-
task.wait(timeout=None, interval=0.5)
|
152
|
+
return handle_task(task, wait, format="script", timeout=3600)
|
442
153
|
|
443
154
|
|
444
155
|
class Ping(Command):
|
osism/services/listener.py
CHANGED
@@ -40,7 +40,6 @@ class BaremetalEvents:
|
|
40
40
|
},
|
41
41
|
"maintenance_set": {"end": self.node_maintenance_set_end},
|
42
42
|
"provision_set": {
|
43
|
-
"start": self.node_provision_set_start,
|
44
43
|
"end": self.node_provision_set_end,
|
45
44
|
"success": self.node_provision_set_success,
|
46
45
|
},
|
@@ -97,28 +96,6 @@ class BaremetalEvents:
|
|
97
96
|
)
|
98
97
|
netbox.set_maintenance.delay(name, object_data["maintenance"])
|
99
98
|
|
100
|
-
def node_provision_set_start(self, payload: dict[Any, Any]) -> None:
|
101
|
-
object_data = self.get_object_data(payload)
|
102
|
-
name = object_data["name"]
|
103
|
-
logger.info(
|
104
|
-
f"baremetal.node.provision_set.start ## {name} ## {object_data['provision_state']}"
|
105
|
-
)
|
106
|
-
|
107
|
-
if object_data["event"] == "inspect":
|
108
|
-
# system should be in state a
|
109
|
-
netbox.connect.delay(name, "a")
|
110
|
-
|
111
|
-
if object_data["provision_state"] == "cleaning":
|
112
|
-
# system should be in state b
|
113
|
-
netbox.connect.delay(name, "b")
|
114
|
-
|
115
|
-
if object_data["provision_state"] == "available":
|
116
|
-
# system should be in state c
|
117
|
-
netbox.connect.delay(name, "c")
|
118
|
-
|
119
|
-
if object_data["target_provision_state"] == "active":
|
120
|
-
pass
|
121
|
-
|
122
99
|
def node_provision_set_success(self, payload: dict[Any, Any]) -> None:
|
123
100
|
# A provision status was successfully set, update it in the netbox
|
124
101
|
object_data = self.get_object_data(payload)
|
@@ -128,10 +105,6 @@ class BaremetalEvents:
|
|
128
105
|
)
|
129
106
|
netbox.set_state.delay(name, object_data["provision_state"], "provision")
|
130
107
|
|
131
|
-
if object_data["provision_state"] == "manageable":
|
132
|
-
# system should be in state c
|
133
|
-
netbox.connect.delay(name, "c")
|
134
|
-
|
135
108
|
def node_provision_set_end(self, payload: dict[Any, Any]) -> None:
|
136
109
|
object_data = self.get_object_data(payload)
|
137
110
|
name = object_data["name"]
|
@@ -147,16 +120,6 @@ class BaremetalEvents:
|
|
147
120
|
netbox.set_state.delay(name, "introspected", "introspection")
|
148
121
|
openstack.baremetal_set_node_provision_state.delay(name, "provide")
|
149
122
|
|
150
|
-
elif object_data["previous_provision_state"] == "wait call-back":
|
151
|
-
pass
|
152
|
-
|
153
|
-
elif (
|
154
|
-
object_data["previous_provision_state"] == "cleaning"
|
155
|
-
and object_data["provision_state"] == "available"
|
156
|
-
): # noqa
|
157
|
-
# system should be in state c
|
158
|
-
netbox.connect.delay(name, "c")
|
159
|
-
|
160
123
|
def port_create_end(self, payload: dict[Any, Any]) -> None:
|
161
124
|
object_data = self.get_object_data(payload)
|
162
125
|
name = object_data["name"]
|
@@ -206,9 +169,6 @@ class BaremetalEvents:
|
|
206
169
|
netbox.set_state.delay(name, None, "introspection")
|
207
170
|
netbox.set_state.delay(name, None, "deployment")
|
208
171
|
|
209
|
-
# system should be in state a
|
210
|
-
netbox.connect.delay(name, "a")
|
211
|
-
|
212
172
|
# remove internal flavor
|
213
173
|
openstack.baremetal_delete_internal_flavor.delay(name)
|
214
174
|
|
osism/settings.py
CHANGED
@@ -27,9 +27,6 @@ NETBOX_URL = os.getenv("NETBOX_API")
|
|
27
27
|
NETBOX_TOKEN = os.getenv("NETBOX_TOKEN", read_secret("NETBOX_TOKEN"))
|
28
28
|
IGNORE_SSL_ERRORS = os.getenv("IGNORE_SSL_ERRORS", "True") == "True"
|
29
29
|
|
30
|
-
BASE_PATH = os.getenv("BASE_PATH", "/devicetype-library/device-types/")
|
31
|
-
VENDORS = os.getenv("VENDORS", "").split()
|
32
|
-
|
33
30
|
# 43200 seconds = 12 hours
|
34
31
|
GATHER_FACTS_SCHEDULE = float(os.getenv("GATHER_FACTS_SCHEDULE", "43200.0"))
|
35
32
|
INVENTORY_RECONCILER_SCHEDULE = float(
|
osism/tasks/__init__.py
CHANGED
@@ -160,20 +160,6 @@ def run_ansible_in_environment(
|
|
160
160
|
env=env,
|
161
161
|
)
|
162
162
|
|
163
|
-
# execute local netbox playbooks
|
164
|
-
elif worker == "osism-ansible" and environment == "netbox-local":
|
165
|
-
if locking:
|
166
|
-
lock.acquire()
|
167
|
-
|
168
|
-
command = f"/run-{environment}.sh {role} {joined_arguments}"
|
169
|
-
logger.info(f"RUN {command}")
|
170
|
-
p = subprocess.Popen(
|
171
|
-
command,
|
172
|
-
stdout=subprocess.PIPE,
|
173
|
-
stderr=subprocess.STDOUT,
|
174
|
-
shell=True,
|
175
|
-
)
|
176
|
-
|
177
163
|
# execute all other roles
|
178
164
|
else:
|
179
165
|
if locking:
|
@@ -207,6 +193,50 @@ def run_ansible_in_environment(
|
|
207
193
|
return result
|
208
194
|
|
209
195
|
|
196
|
+
def run_command(
|
197
|
+
request_id,
|
198
|
+
command,
|
199
|
+
env,
|
200
|
+
*arguments,
|
201
|
+
publish=True,
|
202
|
+
locking=False,
|
203
|
+
auto_release_time=3600,
|
204
|
+
):
|
205
|
+
result = ""
|
206
|
+
command_env = os.environ.copy()
|
207
|
+
command_env.update(env)
|
208
|
+
|
209
|
+
if locking:
|
210
|
+
lock = Redlock(
|
211
|
+
key=f"lock-{command}",
|
212
|
+
masters={redis},
|
213
|
+
auto_release_time=auto_release_time,
|
214
|
+
)
|
215
|
+
|
216
|
+
p = subprocess.Popen(
|
217
|
+
[command] + list(arguments),
|
218
|
+
env=command_env,
|
219
|
+
stdout=subprocess.PIPE,
|
220
|
+
stderr=subprocess.STDOUT,
|
221
|
+
)
|
222
|
+
while p.poll() is None:
|
223
|
+
line = p.stdout.readline().decode("utf-8")
|
224
|
+
if publish:
|
225
|
+
redis.xadd(request_id, {"type": "stdout", "content": line})
|
226
|
+
result += line
|
227
|
+
|
228
|
+
rc = p.wait(timeout=60)
|
229
|
+
|
230
|
+
if publish:
|
231
|
+
redis.xadd(request_id, {"type": "rc", "content": rc})
|
232
|
+
redis.xadd(request_id, {"type": "action", "content": "quit"})
|
233
|
+
|
234
|
+
if locking:
|
235
|
+
lock.release()
|
236
|
+
|
237
|
+
return result
|
238
|
+
|
239
|
+
|
210
240
|
def handle_task(t, wait=True, format="log", timeout=3600):
|
211
241
|
global redis
|
212
242
|
|