|
@@ -142,9 +142,11 @@ def new_instance(name):
|
142
|
142
|
##@brief Delete an instance
|
143
|
143
|
#@param name str : the instance name
|
144
|
144
|
def delete_instance(name):
|
145
|
|
- if get_pid(name) is not None:
|
|
145
|
+ pids = get_pids()
|
|
146
|
+ if name in pids:
|
146
|
147
|
logging.error("The instance '%s' is started. Stop it before deleting \
|
147
|
148
|
it" % name)
|
|
149
|
+ return
|
148
|
150
|
store_datas = get_store_datas()
|
149
|
151
|
logging.warning("Deleting instance %s" % name)
|
150
|
152
|
logging.info("Deleting instance folder %s" % store_datas[name]['path'])
|
|
@@ -175,7 +177,7 @@ def validate_names(names):
|
175
|
177
|
##@brief Returns the PID dict
|
176
|
178
|
#@return a dict with instance name as key an PID as value
|
177
|
179
|
def get_pids():
|
178
|
|
- if not os.path.isfile(PID_FILE):
|
|
180
|
+ if not os.path.isfile(PID_FILE) or os.stat(PID_FILE).st_size == 0:
|
179
|
181
|
return dict()
|
180
|
182
|
with open(PID_FILE, 'r') as pfd:
|
181
|
183
|
return json.load(pfd)
|
|
@@ -193,11 +195,14 @@ def get_pid(name):
|
193
|
195
|
if name not in pid_datas:
|
194
|
196
|
return False
|
195
|
197
|
else:
|
196
|
|
- return pid_datas[name]
|
|
198
|
+ pid = pid_datas[name]
|
|
199
|
+ if not is_running(name, pid):
|
|
200
|
+ return False
|
|
201
|
+ return pid
|
197
|
202
|
|
198
|
203
|
##@brief Start an instance
|
199
|
204
|
#@param names list : instance name list
|
200
|
|
-def start_instances(names):
|
|
205
|
+def start_instances(names, foreground):
|
201
|
206
|
pids = get_pids()
|
202
|
207
|
store_datas = get_store_datas()
|
203
|
208
|
|
|
@@ -207,11 +212,21 @@ def start_instances(names):
|
207
|
212
|
continue
|
208
|
213
|
os.chdir(store_datas[name]['path'])
|
209
|
214
|
args = [sys.executable, 'loader.py']
|
210
|
|
- curexec = subprocess.Popen(args)
|
211
|
|
- pids[name] = curexec.pid
|
212
|
|
- logging.info("Instance '%s' started. PID %d" % (name, curexec.pid))
|
|
215
|
+ if foreground:
|
|
216
|
+ logging.info("Calling execl with : ", args)
|
|
217
|
+ os.execl(args[0], *args)
|
|
218
|
+ return #only usefull if execl call fails (not usefull)
|
|
219
|
+ else:
|
|
220
|
+ curexec = subprocess.Popen(args,
|
|
221
|
+ stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
|
222
|
+ stderr=subprocess.DEVNULL, preexec_fn=os.setsid,
|
|
223
|
+ cwd = store_datas[name]['path'])
|
|
224
|
+ pids[name] = curexec.pid
|
|
225
|
+ logging.info("Instance '%s' started. PID %d" % (name, curexec.pid))
|
213
|
226
|
save_pids(pids)
|
214
|
227
|
|
|
228
|
+##@brief Stop an instance given its name
|
|
229
|
+#@param names list : names list
|
215
|
230
|
def stop_instances(names):
|
216
|
231
|
pids = get_pids()
|
217
|
232
|
store_datas = get_store_datas()
|
|
@@ -220,7 +235,30 @@ def stop_instances(names):
|
220
|
235
|
logging.warning("The instance %s is not running" % name)
|
221
|
236
|
continue
|
222
|
237
|
pid = pids[name]
|
223
|
|
- os.kill(pid, signal.SIGINT)
|
|
238
|
+ try:
|
|
239
|
+ os.kill(pid, signal.SIGINT)
|
|
240
|
+ except ProcessLookupError:
|
|
241
|
+ logging.warning("The instance %s seems to be in error, no process \
|
|
242
|
+with pid %d found" % (pids[name], name))
|
|
243
|
+ del(pids[name])
|
|
244
|
+ save_pids(pids)
|
|
245
|
+
|
|
246
|
+##@brief Checks that a process is running
|
|
247
|
+#
|
|
248
|
+#If not running clean the pid list
|
|
249
|
+#@return bool
|
|
250
|
+def is_running(name, pid):
|
|
251
|
+ try:
|
|
252
|
+ os.kill(pid, 0)
|
|
253
|
+ return True
|
|
254
|
+ except (OSError,ProcessLookupError):
|
|
255
|
+ pid_datas = get_pids()
|
|
256
|
+ logging.warning("Instance '%s' was marked as running, but not \
|
|
257
|
+process with pid %d found. Cleaning pid list" % (name, pid))
|
|
258
|
+ del(pid_datas[name])
|
|
259
|
+ save_pids(pid_datas)
|
|
260
|
+ return False
|
|
261
|
+
|
224
|
262
|
|
225
|
263
|
##@brief Check if instance are specified
|
226
|
264
|
def get_specified(args):
|
|
@@ -269,7 +307,7 @@ def details_instance(name, verbosity, batch):
|
269
|
307
|
pids = get_pids()
|
270
|
308
|
if not batch:
|
271
|
309
|
msg = "\t- '%s'" % name
|
272
|
|
- if name in pids:
|
|
310
|
+ if name in pids and is_running(name, pids[name]):
|
273
|
311
|
msg += ' [Run PID %d] ' % pids[name]
|
274
|
312
|
if verbosity > 0:
|
275
|
313
|
msg += ' path = "%s"' % store_datas[name]['path']
|
|
@@ -302,6 +340,7 @@ def get_parser():
|
302
|
340
|
selector = parser.add_argument_group('Instances selectors')
|
303
|
341
|
actions = parser.add_argument_group('Instances actions')
|
304
|
342
|
confs = parser.add_argument_group('Options (use with -c or -s)')
|
|
343
|
+ startstop = parser.add_argument_group('Start/stop options')
|
305
|
344
|
|
306
|
345
|
parser.add_argument('-l', '--list',
|
307
|
346
|
help='list existing instances and exit', action='store_const',
|
|
@@ -335,14 +374,18 @@ def get_parser():
|
335
|
374
|
actions.add_argument('-i', '--interactive', action='store_const',
|
336
|
375
|
default=False, const=True,
|
337
|
376
|
help='Run a loader.py from ONE instance in foreground')
|
338
|
|
- actions.add_argument('--stop', action='store_const',
|
339
|
|
- default=False, const=True, help="Stop instances")
|
340
|
|
- actions.add_argument('--start', action='store_const',
|
341
|
|
- default=False, const=True, help="Start instances")
|
342
|
377
|
actions.add_argument('-m', '--make', metavar='TARGET', type=str,
|
343
|
378
|
nargs="?", default='not',
|
344
|
379
|
help='Run make for selected instances')
|
345
|
380
|
|
|
381
|
+ startstop.add_argument('--stop', action='store_const',
|
|
382
|
+ default=False, const=True, help="Stop instances")
|
|
383
|
+ startstop.add_argument('--start', action='store_const',
|
|
384
|
+ default=False, const=True, help="Start instances")
|
|
385
|
+ startstop.add_argument('-f', '--foreground', action='store_const',
|
|
386
|
+ default=False, const=True, help="Start in foreground (limited \
|
|
387
|
+to 1 instance")
|
|
388
|
+
|
346
|
389
|
confs.add_argument('--interface', type=str,
|
347
|
390
|
help="Select wich interface to run. Possible values are \
|
348
|
391
|
'python' and 'web'")
|
|
@@ -355,6 +398,8 @@ def get_parser():
|
355
|
398
|
if __name__ == '__main__':
|
356
|
399
|
parser = get_parser()
|
357
|
400
|
args = parser.parse_args()
|
|
401
|
+ if args.verbose is None:
|
|
402
|
+ args.verbose = 0
|
358
|
403
|
if args.list:
|
359
|
404
|
# Instances list
|
360
|
405
|
if args.name is not None:
|
|
@@ -373,9 +418,9 @@ if __name__ == '__main__':
|
373
|
418
|
for name in args.name:
|
374
|
419
|
new_instance(name)
|
375
|
420
|
elif args.purge:
|
376
|
|
- print("Are you sure ? Yes/no ",)
|
377
|
|
- rep = sys.stdin.read()
|
378
|
|
- if rep == 'Yes':
|
|
421
|
+ print("Do you really want to delete all the instances ? Yes/no ",)
|
|
422
|
+ rep = sys.stdin.readline()
|
|
423
|
+ if rep == "Yes\n":
|
379
|
424
|
store = get_store_datas()
|
380
|
425
|
for name in store:
|
381
|
426
|
delete_instance(name)
|
|
@@ -440,15 +485,23 @@ specified")
|
440
|
485
|
elif args.name is not None:
|
441
|
486
|
names = args.name
|
442
|
487
|
if names is None:
|
|
488
|
+ parser.print_help()
|
443
|
489
|
print("\n-s option only allowed with instance specified (by name \
|
444
|
490
|
or with -a)")
|
445
|
|
- parser.print_help()
|
446
|
491
|
exit(1)
|
447
|
492
|
for name in names:
|
448
|
493
|
set_conf(name, args)
|
449
|
494
|
elif args.start:
|
450
|
495
|
names = get_specified(args)
|
451
|
|
- start_instances(names)
|
|
496
|
+ if names is None:
|
|
497
|
+ parser.print_help()
|
|
498
|
+ print("\nPlease specify at least 1 instance with the --start \
|
|
499
|
+option", file=sys.stderr)
|
|
500
|
+ elif args.foreground and len(names) > 1:
|
|
501
|
+ parser.prin_help()
|
|
502
|
+ print("\nOnly 1 instance allowed with the use of the --forground \
|
|
503
|
+argument")
|
|
504
|
+ start_instances(names, args.foreground)
|
452
|
505
|
elif args.stop:
|
453
|
506
|
names = get_specified(args)
|
454
|
507
|
stop_instances(names)
|