Blame view

pyros.py 21.8 KB
efbcac9e   Etienne Pallier   1st version of NE...
1
2
#!/usr/bin/env python3

ddb06a2a   Etienne Pallier   new pyros2 launch...
3
4
5

import argparse
import fileinput
f3d076fb   Etienne Pallier   Génération automa...
6
import glob
efbcac9e   Etienne Pallier   1st version of NE...
7
import os
efbcac9e   Etienne Pallier   1st version of NE...
8
import platform
efbcac9e   Etienne Pallier   1st version of NE...
9
import signal
ddb06a2a   Etienne Pallier   new pyros2 launch...
10
11
12
import subprocess
import sys
import time
efbcac9e   Etienne Pallier   1st version of NE...
13

1f6376dd   Etienne Pallier   pyros2 refactor &...
14

1f6376dd   Etienne Pallier   pyros2 refactor &...
15
16
17
18
19
20
21

"""
*****************************************************************
******************** GENERAL CONSTANTS **************************
*****************************************************************
"""

f3d076fb   Etienne Pallier   Génération automa...
22
DEBUG = False
a3015e31   Etienne Pallier   table des commandes
23

ad6d274a   Etienne Pallier   Restructuration d...
24
25
PYROS_DJANGO_BASE_DIR = "src/core/pyros_django"

a3015e31   Etienne Pallier   table des commandes
26
INIT_FIXTURE = "initial_fixture.json"
01348735   Etienne Pallier   Bugfix pyros.py s...
27

ad6d274a   Etienne Pallier   Restructuration d...
28
29
_previous_dir = None

eac3e2ab   Etienne Pallier   New AgentX
30
AGENTS = {
a7887020   Etienne Pallier   GROSSE OPTIM : pl...
31
    #"agentX" : "agent", 
ff0550d2   Etienne Pallier   Multi-agents : Ag...
32
    "agent" : "Agent", 
a7887020   Etienne Pallier   GROSSE OPTIM : pl...
33
34
    "agentX" : "AgentX", 
    "agentA" : "AgentA", 
c84ca326   Etienne Pallier   Multi-agents (3 a...
35
    "agentB" : "AgentB", 
5b835b38   Etienne Pallier   agentM
36
    "agentM" : "AgentM", 
617aeab8   Etienne Pallier   Ajout table uniqu...
37
    #"agentDevice" : "AgentDevice", 
4783e5b5   Etienne Pallier   GROS RENOMMAGE de...
38
39
    "agentDeviceTelescopeGemini" : "AgentDeviceTelescopeGemini",
    "agentDeviceSBIG" : "AgentDeviceSBIG",
128e1a71   Etienne Pallier   AgentDevice + Age...
40
    "agentTelescopeRequester" : "AgentTelescopeRequester", 
4783e5b5   Etienne Pallier   GROS RENOMMAGE de...
41
    "agentMultiRequester" : "AgentMultiRequester", 
eac3e2ab   Etienne Pallier   New AgentX
42
43
44
45
46
47
48
    "webserver" : "webserver", 
    "monitoring" : "monitoring", 
    "majordome" : "majordome", 
    "scheduler" : "scheduler", 
    "alert_manager" : "alert_manager"
}
#AGENTS = ["agentX", "webserver", "monitoring", "majordome", "scheduler", "alert_manager"]
1f6376dd   Etienne Pallier   pyros2 refactor &...
49
50
51
52
#AGENTS = ["all", "webserver", "monitoring", "majordome", "scheduler", "alert"]

#COMMANDS = {"install": [], "start": AGENTS, "stop": AGENTS}

082ccda3   Etienne Pallier   pyros.py : enrich...
53
54
55
56
57
REQUIREMENTS = 'REQUIREMENTS.txt'
b_in_dir = "bin"
PYTHON = "python3"
# should also be ok from venv:
#PYTHON = "python"
1f6376dd   Etienne Pallier   pyros2 refactor &...
58

082ccda3   Etienne Pallier   pyros.py : enrich...
59
IS_WINDOWS = platform.system() == "Windows"
1f6376dd   Etienne Pallier   pyros2 refactor &...
60
if IS_WINDOWS:
082ccda3   Etienne Pallier   pyros.py : enrich...
61
    REQUIREMENTS = 'REQUIREMENTS_WINDOWS.txt'
a3015e31   Etienne Pallier   table des commandes
62
    b_in_dir = "Scripts"
1f6376dd   Etienne Pallier   pyros2 refactor &...
63
    PYTHON = "python.exe"
082ccda3   Etienne Pallier   pyros.py : enrich...
64
65

my_abs_path = os.path.dirname(os.path.realpath(__file__))
1f6376dd   Etienne Pallier   pyros2 refactor &...
66
67
VENV_BIN = (
    my_abs_path
77f1c546   Etienne Pallier   Ajouté options "t...
68
    + os.sep + "venv"
1f6376dd   Etienne Pallier   pyros2 refactor &...
69
    + os.sep + "venv_py3_pyros"
a3015e31   Etienne Pallier   table des commandes
70
    + os.sep + b_in_dir
1f6376dd   Etienne Pallier   pyros2 refactor &...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    + os.sep + PYTHON
)

class Colors:
    HEADER = "\033[95m"
    BLUE = "\033[94m"
    GREEN = "\033[92m"
    WARNING = "\033[93m"
    FAIL = "\033[91m"
    ENDC = "\033[0m"
    BOLD = "\033[1m"
    UNDERLINE = "\033[4m"




ddb06a2a   Etienne Pallier   new pyros2 launch...
87
88
89
90
91
# First, install the click package !!!
#import fire
try:
    import click # https://click.palletsprojects.com
except:
1f6376dd   Etienne Pallier   pyros2 refactor &...
92
93
    #pip = "pip" if platform.system() == "Windows" else "pip3"
    pip = "pip" if IS_WINDOWS else "pip3"
ddb06a2a   Etienne Pallier   new pyros2 launch...
94
95
96
97
98
99
100
101
102
103
104
    process = subprocess.Popen(pip + " install --upgrade click", shell=True)
    process.wait()
    if process.returncode == 0:
        print("click package installation successfull")
        # self.addExecuted(self.current_command, command)
        import click
    else:
        print("click package installation failed")
        # self.addError(self.current_command, command)
        
    
efbcac9e   Etienne Pallier   1st version of NE...
105

efbcac9e   Etienne Pallier   1st version of NE...
106
107


efbcac9e   Etienne Pallier   1st version of NE...
108

1f6376dd   Etienne Pallier   pyros2 refactor &...
109
110
111
112
113
"""
**************************************************************************
******************** GENERAL AND UTIL FUNCTIONS **************************
**************************************************************************
"""
efbcac9e   Etienne Pallier   1st version of NE...
114

a3015e31   Etienne Pallier   table des commandes
115
def _in_dir(dirname: str = ""):
b2e9866d   Etienne Pallier   bugfix start_agen...
116
    return os.path.basename(os.getcwd()) == dirname
efbcac9e   Etienne Pallier   1st version of NE...
117

efbcac9e   Etienne Pallier   1st version of NE...
118

a3015e31   Etienne Pallier   table des commandes
119
def _in_abs_dir(dirname: str = ""):
b2e9866d   Etienne Pallier   bugfix start_agen...
120
    return os.getcwd() == dirname
efbcac9e   Etienne Pallier   1st version of NE...
121
122


a3015e31   Etienne Pallier   table des commandes
123
124
125
def die(msg: str = ""):
    print()
    print("...ERROR...")
b2e9866d   Etienne Pallier   bugfix start_agen...
126
    print(msg)
a3015e31   Etienne Pallier   table des commandes
127
    print()
b2e9866d   Etienne Pallier   bugfix start_agen...
128
    exit(1)
a3015e31   Etienne Pallier   table des commandes
129
    
efbcac9e   Etienne Pallier   1st version of NE...
130

1f6376dd   Etienne Pallier   pyros2 refactor &...
131
132
#TODO: implement is_async
def execProcess(command, from_venv=False, is_async=False):
67f74b9e   Etienne Pallier   cleanup
133
    from_venv_str = " from venv ("+VENV_BIN+")" if from_venv else ""
f3d076fb   Etienne Pallier   Génération automa...
134
135
    #printFullTerm(Colors.BLUE, "Executing command" + " [" + command + "]" + from_venv_str)
    print("Executing command" + " [" + command + "]" + from_venv_str)
1f6376dd   Etienne Pallier   pyros2 refactor &...
136
    if from_venv: command = VENV_BIN+' ' + command
b2e9866d   Etienne Pallier   bugfix start_agen...
137
    process = subprocess.Popen(command, shell=True)
31e31f3b   Etienne Pallier   pyros.py peut lan...
138
139
140
141
    if is_async:
        #current_processes.append(process)
        return process
    # Not is_async => Wait for end of execution
b2e9866d   Etienne Pallier   bugfix start_agen...
142
143
144
145
146
147
148
    process.wait()
    if process.returncode == 0:
        printFullTerm(Colors.GREEN, "Process executed successfully")
        # self.addExecuted(self.current_command, command)
    else:
        printFullTerm(Colors.WARNING, "Process execution failed")
        # self.addError(self.current_command, command)
f4fd20f9   Etienne Pallier   pyros.py script i...
149
150
    #return process.returncode
    return True if process.returncode==0 else False
efbcac9e   Etienne Pallier   1st version of NE...
151

31e31f3b   Etienne Pallier   pyros.py peut lan...
152
153
154
def execProcessFromVenv(command:str, is_async=False):
    #return execProcess(command, from_venv=True, is_async)
    return execProcess(command, True, is_async)
efbcac9e   Etienne Pallier   1st version of NE...
155

1f6376dd   Etienne Pallier   pyros2 refactor &...
156
157
#TODO: fusionner dans execProcess avec param is_async
def execProcessFromVenvAsync(command:str):
31e31f3b   Etienne Pallier   pyros.py peut lan...
158
159
    return execProcessFromVenv(command, True)
    """
b2e9866d   Etienne Pallier   bugfix start_agen...
160
161
162
163
164
165
166
167
168
169
    args = command.split()
    printFullTerm(
        Colors.BLUE, "Executing command from venv [" + str(" ".join(args[1:])) + "]"
    )
    p = subprocess.Popen(args)
    subproc.append((p, " ".join(args[1:])))
    printFullTerm(Colors.GREEN, "Process launched successfully")
    # self.addExecuted(self.current_command, str(' '.join(args[1:])))
    # p.wait()
    return p
31e31f3b   Etienne Pallier   pyros.py peut lan...
170
    """
efbcac9e   Etienne Pallier   1st version of NE...
171

b2e9866d   Etienne Pallier   bugfix start_agen...
172
def printColor(color: Colors, message, file=sys.stdout, eol=os.linesep, forced=False):
1f6376dd   Etienne Pallier   pyros2 refactor &...
173
    #system = platform.system()
b2e9866d   Etienne Pallier   bugfix start_agen...
174
175
    """
    if (self.disp == False and forced == False):
efbcac9e   Etienne Pallier   1st version of NE...
176
        return 0
b2e9866d   Etienne Pallier   bugfix start_agen...
177
    """
1f6376dd   Etienne Pallier   pyros2 refactor &...
178
179
    #if system == "Windows":
    if IS_WINDOWS:
b2e9866d   Etienne Pallier   bugfix start_agen...
180
181
182
183
        print(message, file=file, end=eol)
    else:
        print(color + message + Colors.ENDC, file=file, end=eol)
    return 0
efbcac9e   Etienne Pallier   1st version of NE...
184
185


b2e9866d   Etienne Pallier   bugfix start_agen...
186
def printFullTerm(color: Colors, string: str):
1f6376dd   Etienne Pallier   pyros2 refactor &...
187
    #system = platform.system()
b2e9866d   Etienne Pallier   bugfix start_agen...
188
189
190
191
192
193
194
195
196
    columns = 100
    row = 1000
    disp = True
    value = int(columns / 2 - len(string) / 2)
    printColor(color, "-" * value, eol="")
    printColor(color, string, eol="")
    value += len(string)
    printColor(color, "-" * (columns - value))
    return 0
efbcac9e   Etienne Pallier   1st version of NE...
197

efbcac9e   Etienne Pallier   1st version of NE...
198

f4fd20f9   Etienne Pallier   pyros.py script i...
199
200


ddb06a2a   Etienne Pallier   new pyros2 launch...
201
202

"""
1f6376dd   Etienne Pallier   pyros2 refactor &...
203
204
205
********************************************************************************
******************** CLI COMMANDS DEFINITION (click format) ********************
********************************************************************************
ddb06a2a   Etienne Pallier   new pyros2 launch...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
"""

'''
_global_test_options = [
    click.option('--test', '-t', is_flag=True, help="don't do it for real, just show what it would do"),
    click.option('--verbose', '-v', 'verbosity', flag_value=2, default=1, help='Verbose output'),
    click.option('--quiet', '-q', 'verbosity', flag_value=0, help='Minimal output'),
    #click.option('--fail-fast', '--failfast', '-f', 'fail_fast', is_flag=True, default=False, help='Stop on failure'),
]
def global_test_options(func):
    for option in reversed(_global_test_options):
        func = option(func)
    return func
'''

GLOBAL_OPTIONS = {}
def verbose_mode(): return GLOBAL_OPTIONS["verbose"]
def test_mode(): return GLOBAL_OPTIONS["test"]
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
224
def sim_mode(): return GLOBAL_OPTIONS["sim"]
ddb06a2a   Etienne Pallier   new pyros2 launch...
225
226
227

@click.group()
@click.option('--test', '-t', is_flag=True, help="don't do it for real, just show what it would do")
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
228
@click.option('--sim', '-s', is_flag=True, help="only for an AgentDevice, asking it to start its simulator and work with it (instead of real device)")
ddb06a2a   Etienne Pallier   new pyros2 launch...
229
230
231
232
@click.option('--verbose', '-v', is_flag=True, help='Verbose output')
#@click.option('--verbose', '-v', 'verbosity', flag_value=2, default=1, help='Verbose output'),
#@click.option('--quiet', '-q', 'verbosity', flag_value=0, help='Minimal output'),
#@click.option('--fail-fast', '--failfast', '-f', 'fail_fast', is_flag=True, default=False, help='Stop on failure'),
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
233
def pyros_launcher(test, sim, verbose):
ddb06a2a   Etienne Pallier   new pyros2 launch...
234
235
    #pass
    if test: click.echo('Test mode')
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
236
    if test: click.echo('Starting and connecting to simulator instead of real device')
ddb06a2a   Etienne Pallier   new pyros2 launch...
237
238
    if verbose: click.echo('Verbose mode')
    GLOBAL_OPTIONS["test"] = test
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
239
    GLOBAL_OPTIONS["sim"] = sim
ddb06a2a   Etienne Pallier   new pyros2 launch...
240
241
242
243
    GLOBAL_OPTIONS["verbose"] = verbose



af5d8b11   Etienne Pallier   Nouveau shell pyr...
244
245
246
247
248
@pyros_launcher.command(help="Run a pyros shell (django included)")
#@global_test_options
def shell():
    print()
    print("Launching a pyros shell")
17ad5e75   Etienne Pallier   updated message d...
249
250
251
252
253
    print()
    print("NB1: If you want to play with an agent, type:")
    print("    >>> from agent.AgentA import AgentA")
    print("    >>> agent=AgentA('agent_toto')")
    print("    >>> agent")
15afe6e1   Etienne Pallier   bugfix Agent.run(...
254
    print("    >>> agent.run(2) (=> will run 2 iterations)")
17ad5e75   Etienne Pallier   updated message d...
255
256
    print("    >>> cmd = agent.send_command('AgentB','eval 2+2')")
    print("    >>> cmd")
ad6d274a   Etienne Pallier   Restructuration d...
257
    print("    >>> cmd.get_updated_result()")
15afe6e1   Etienne Pallier   bugfix Agent.run(...
258
259
    print("    >>> ...")
    print("    - See documentation, section 'Play with a pyros agent' inside the chapter 'Running pyros' for more details")
17ad5e75   Etienne Pallier   updated message d...
260
261
262
263
264
265
266
267
    print()
    print("NB2: If you want to play with the pyros objects, type:")
    print("    >>> from common.models import *")
    print("    - (This will import all the pyros objects)")
    print("    - Then, you can create any pyros object just by typing its name.")
    print("    - For example, to create an AgentSurvey object, type:")
    print("    >>> agent_survey = AgentSurvey()")
    print("    >>> agent_survey")
15afe6e1   Etienne Pallier   bugfix Agent.run(...
268
269
    print("    >>> ...")
    print("    - See documentation, section 'Play with the pyros objects' inside the chapter 'Running pyros' for more details")
17ad5e75   Etienne Pallier   updated message d...
270
    print()
af5d8b11   Etienne Pallier   Nouveau shell pyr...
271
272
    print("Type 'exit()' to quit")
    print()
ad6d274a   Etienne Pallier   Restructuration d...
273
274
    change_dir(PYROS_DJANGO_BASE_DIR)

af5d8b11   Etienne Pallier   Nouveau shell pyr...
275
    # execProcess("python install.py install")
12a0badc   Etienne Pallier   update
276
    if not test_mode(): execProcessFromVenv("manage.py shell")
af5d8b11   Etienne Pallier   Nouveau shell pyr...
277
    # Go back to the initial dir
ad6d274a   Etienne Pallier   Restructuration d...
278
279
    #os.chdir("../")
    change_dir("PREVIOUS")
af5d8b11   Etienne Pallier   Nouveau shell pyr...
280
281
282
    return True


fcfc6200   Etienne Pallier   ajout nouvelle co...
283
284
285
286
@pyros_launcher.command(help="Run a database (Mysql) shell")
def dbshell():
    print()
    print("Launching a database (mysql) shell")
082ccda3   Etienne Pallier   pyros.py : enrich...
287
    print("From this shell, you can type 'show tables;' to see all the pyros tables")
fcfc6200   Etienne Pallier   ajout nouvelle co...
288
289
290
291
    print("Then for example, type 'select * from config;' to see the content of the 'config' table")
    print("Type 'exit' to quit")
    print()
    # execProcess("python install.py install")
ad6d274a   Etienne Pallier   Restructuration d...
292
    if not test_mode(): execProcessFromVenv(PYROS_DJANGO_BASE_DIR+"/manage.py dbshell")
fcfc6200   Etienne Pallier   ajout nouvelle co...
293
294
295
296
    # Go back to the initial dir
    return True


af5d8b11   Etienne Pallier   Nouveau shell pyr...
297
@pyros_launcher.command(help="Install the pyros software")
3a36c853   Etienne Pallier   "./pyros install ...
298
299
@click.option('--packages_only', '-p', is_flag=True, help='install only the python packages (no database installation)')
@click.option('--database_only', '-d', is_flag=True, help='install only the pyros database (no python packages installation)')
ddb06a2a   Etienne Pallier   new pyros2 launch...
300
#@global_test_options
3a36c853   Etienne Pallier   "./pyros install ...
301
def install(packages_only, database_only):
1f6376dd   Etienne Pallier   pyros2 refactor &...
302
    print("Running install command")
3a36c853   Etienne Pallier   "./pyros install ...
303
304
    print("packages_only", packages_only)
    print("database_only", database_only)
ddb06a2a   Etienne Pallier   new pyros2 launch...
305
306
307
    #if test_mode(): print("in test mode")
    # self.execProcess("python3 install/install.py install")
    # if (os.path.basename(os.getcwd()) != "private"):
ddb06a2a   Etienne Pallier   new pyros2 launch...
308
    start_dir = os.getcwd()
a3015e31   Etienne Pallier   table des commandes
309
    os.chdir("install/") ; _in_dir("install") or die("Bad dir")
1f6376dd   Etienne Pallier   pyros2 refactor &...
310
    # execProcess("python install.py install")
3a36c853   Etienne Pallier   "./pyros install ...
311
312
313
314
    option=''
    if packages_only: option='-p'
    if database_only: option='-d'
    test_mode() or execProcess(PYTHON + " install.py " + option)
ddb06a2a   Etienne Pallier   new pyros2 launch...
315
316
    # cd -
    # os.chdir("-")
a3015e31   Etienne Pallier   table des commandes
317
    os.chdir(start_dir) ; _in_abs_dir(start_dir) or die("Bad dir")
ddb06a2a   Etienne Pallier   new pyros2 launch...
318
319
320
    # return 0
    return True

f4fd20f9   Etienne Pallier   pyros.py script i...
321
322


77f1c546   Etienne Pallier   Ajouté options "t...
323
324
325
326
327
328
329
@pyros_launcher.command(help="Run some tests")
def test():
    print("Running tests")
    #start_dir = os.getcwd()
    apps = ['common', 'scheduler', 'routine_manager', 'user_manager', 'alert_manager.tests.TestStrategyChange']
    for app in apps:
        _loaddata() or die()
ad6d274a   Etienne Pallier   Restructuration d...
330
        change_dir(PYROS_DJANGO_BASE_DIR)
80d4ac71   Etienne Pallier   updated version a...
331
332
333
334
        # Delete test_pyros database after tests
        #execProcessFromVenv('manage.py test ' + app) or die()
        # KEEP test_pyros database after tests
        execProcessFromVenv('manage.py test --keep ' + app) or die()
ad6d274a   Etienne Pallier   Restructuration d...
335
        change_dir("PREVIOUS")
77f1c546   Etienne Pallier   Ajouté options "t...
336
337
338
339
340
    # execProcess("python install.py install")
    return True

@pyros_launcher.command(help="Run ALL tests")
def testall():
ad6d274a   Etienne Pallier   Restructuration d...
341
    change_dir(PYROS_DJANGO_BASE_DIR)
80d4ac71   Etienne Pallier   updated version a...
342
343
344
345
    # Delete test_pyros database after tests
    # execProcessFromVenvAsync("manage.py test")
    # KEEP test_pyros database after tests
    execProcessFromVenvAsync("manage.py test --keep")
ad6d274a   Etienne Pallier   Restructuration d...
346
    change_dir("PREVIOUS")
77f1c546   Etienne Pallier   Ajouté options "t...
347
348
349
350
    return True



f4fd20f9   Etienne Pallier   pyros.py script i...
351
    
01348735   Etienne Pallier   Bugfix pyros.py s...
352
353
354
'''
TODO:
'''
30b22ee6   Etienne Pallier   Agent : agentX wo...
355
@pyros_launcher.command(help="Update (only if necessary) the python packages AND the source code AND the DB structure")
01348735   Etienne Pallier   Bugfix pyros.py s...
356
357
def update():
    print("Running update command")
f3d076fb   Etienne Pallier   Génération automa...
358

f936f035   Etienne Pallier   bugfix update com...
359
    # 1) Update source code (git pull)
f3d076fb   Etienne Pallier   Génération automa...
360
    printFullTerm(Colors.BLUE, "1) UPDATING SOURCE CODE: Running git pull")
30b22ee6   Etienne Pallier   Agent : agentX wo...
361
    _gitpull() or die()
f3d076fb   Etienne Pallier   Génération automa...
362
    
f936f035   Etienne Pallier   bugfix update com...
363
    # 2) Update python packages (pip install requirements)
f3d076fb   Etienne Pallier   Génération automa...
364
    printFullTerm(Colors.BLUE, "2) UPDATING PYTHON PACKAGES")
f936f035   Etienne Pallier   bugfix update com...
365
    _update_python_packages_from_requirements() or die()
f3d076fb   Etienne Pallier   Génération automa...
366
367
368
369
370
371
372
    
    # 3) Update PlantUML diagrams
    printFullTerm(Colors.BLUE, "3) UPDATING PLANTUML DIAGRAMS")
    _update_plantuml_diags() or die()
    
    # 4) Update database structure (make migrations + migrate)
    printFullTerm(Colors.BLUE, "4) UPDATING DATABASE")
f4fd20f9   Etienne Pallier   pyros.py script i...
373
    _updatedb() or die()
01348735   Etienne Pallier   Bugfix pyros.py s...
374
375
    return True

01348735   Etienne Pallier   Bugfix pyros.py s...
376

f4fd20f9   Etienne Pallier   pyros.py script i...
377
378
379
380
381
def _gitpull():
    print("-- running git pull")
    GIT = "git.exe" if IS_WINDOWS else "git"
    if not test_mode(): return execProcess(f"{GIT} pull")
    return True
01348735   Etienne Pallier   Bugfix pyros.py s...
382

f4fd20f9   Etienne Pallier   pyros.py script i...
383
384
385
386
387
388
389
#@pyros_launcher.command(help="Update the pyros database")
def _updatedb():
    print("-- update db (make migrations + migrate)")
    if not test_mode() :
        _makemigrations() or die()
        _migrate() or die()
    return True
ddb06a2a   Etienne Pallier   new pyros2 launch...
390
391


f4fd20f9   Etienne Pallier   pyros.py script i...
392
393
394
395
396
397
398
@pyros_launcher.command(help="Update the pyros database and fill it with initial fixture data")
def initdb():
    if not test_mode():
        #updatedb()
        _makemigrations()
        _migrate()
        _loaddata()
ddb06a2a   Etienne Pallier   new pyros2 launch...
399
400
401
    return True


f4fd20f9   Etienne Pallier   pyros.py script i...
402

ddb06a2a   Etienne Pallier   new pyros2 launch...
403
404
405
406
407
408
409
410
@pyros_launcher.command(help="Launch an agent")
#@global_test_options
@click.argument('agent')
@click.option('--configfile', '-c', help='the configuration file to be used')
#@click.option('--format', '-f', type=click.Choice(['html', 'xml', 'text']), default='html', show_default=True)
#@click.option('--port', default=8000)
#def start(agent:str, configfile:str, test, verbosity):
def start(agent:str, configfile:str):
1f6376dd   Etienne Pallier   pyros2 refactor &...
411
    print("Running start command")
112f32f4   Etienne Pallier   start_agent_agent...
412
413
414
    if configfile: 
        print("With config file", configfile)
    else: 
ad6d274a   Etienne Pallier   Restructuration d...
415
        configfile = ''
ddb06a2a   Etienne Pallier   new pyros2 launch...
416
417
    #if test_mode(): print("in test mode")
    #if verbose_mode(): print("in verbose mode")
7d679ea0   Etienne Pallier   Commande EVAL ok
418
419
420
421
422
423
424
425
426
    
    # Check if multiple agents:
    agents = None
    if "," in agent:
        agents = agent.split(",")
        for a in agents: 
            if not _check_agent(a): return
        print("Agents are:", agents)
    # 1 agent only
31e31f3b   Etienne Pallier   pyros.py peut lan...
427
428
429
430
431
432
    else:
        agents = [agent]
        if not _check_agent(agent): return

    # Start Agents (processes)
    current_processes = []
eac3e2ab   Etienne Pallier   New AgentX
433
    for agent_name,agent_folder in AGENTS.items():
112f32f4   Etienne Pallier   start_agent_agent...
434

31e31f3b   Etienne Pallier   pyros.py peut lan...
435
436
        #if agent in ("all", agent_name) :
        if agent=="all" or agent_name in agents:
112f32f4   Etienne Pallier   start_agent_agent...
437
            # Default case, launch agentX
630dd6e4   Etienne Pallier   Renommé script st...
438
439
440
441
442
443
444
            #if agent_name == "agentX":

            # execProcessFromVenvAsync(VENV_BIN + " manage.py runserver")
            print(VENV_BIN)
            print("Launching agent", agent_name, "...")
            #if not test_mode(): execProcess(VENV_BIN + " manage.py runserver")
            #if not test_mode(): execProcessFromVenv("start_agent_" + agent_name + ".py " + configfile)
a7887020   Etienne Pallier   GROSSE OPTIM : pl...
445
            
31e31f3b   Etienne Pallier   pyros.py peut lan...
446
447
            current_dir = os.getcwd()

a7887020   Etienne Pallier   GROSSE OPTIM : pl...
448
            # OLD format agents: majordome, monitoring, alert...
630dd6e4   Etienne Pallier   Renommé script st...
449
            cmd = "start_agent.py " + agent_name + " " + configfile
a7887020   Etienne Pallier   GROSSE OPTIM : pl...
450
451
            
            # Agent "webserver"
630dd6e4   Etienne Pallier   Renommé script st...
452
453
            if agent_name == "webserver": 
                cmd = "manage.py runserver"
ad6d274a   Etienne Pallier   Restructuration d...
454
                os.chdir(PYROS_DJANGO_BASE_DIR)
630dd6e4   Etienne Pallier   Renommé script st...
455
            #if not test_mode(): execProcessFromVenv("start_agent.py " + agent_name + " " + configfile)
471cc5b9   Etienne Pallier   Déplacé AgentM da...
456

bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
457
            # Agent "agentM", "agentA", "agentB", "agentX", ...            
471cc5b9   Etienne Pallier   Déplacé AgentM da...
458
            elif agent_name.startswith("agent"):
a7887020   Etienne Pallier   GROSSE OPTIM : pl...
459
460
                # Run agent without actual commands sent to devices (FOR_REAL=False)
                ##agentX.run(FOR_REAL=True)
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
461
462
463
464
                if agent_name == "agentM": 
                    os.chdir(PYROS_DJANGO_BASE_DIR+"/monitoring/")
                else:
                    os.chdir(PYROS_DJANGO_BASE_DIR+"/agent/")
a7887020   Etienne Pallier   GROSSE OPTIM : pl...
465
                #cmd = "-m AgentX"
c7318a4a   Etienne Pallier   Nouveau mode test...
466
467
468
                #cmd = f" Agent{agent_name[5:]}.py {configfile}"
                cmd = f"Agent{agent_name[5:]}.py"
                if test_mode(): cmd += " -t"
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
469
                if sim_mode(): cmd += " -s"
c7318a4a   Etienne Pallier   Nouveau mode test...
470
                if configfile: cmd += " {configfile}"
bfb3eb3a   Etienne Pallier   Ajout du mode "wi...
471
                
c7318a4a   Etienne Pallier   Nouveau mode test...
472
473
474
475
            #if not test_mode(): current_processes.append( [execProcessFromVenvAsync(cmd), agent_name, -1] )
            # Append this process ( [process id, agent_name, result=failure] )
            # ("result" will be updated at the end of execution)
            current_processes.append( [execProcessFromVenvAsync(cmd), agent_name, -1] )
f3d076fb   Etienne Pallier   Génération automa...
476
            # self.change_dir("..")
31e31f3b   Etienne Pallier   pyros.py peut lan...
477
            os.chdir(current_dir)
ddb06a2a   Etienne Pallier   new pyros2 launch...
478

b2e9866d   Etienne Pallier   bugfix start_agen...
479
    # Go back to root folder (/)
f3d076fb   Etienne Pallier   Génération automa...
480
    # self.change_dir('..')
31e31f3b   Etienne Pallier   pyros.py peut lan...
481
482
    #os.chdir("..")
    # Wait for end of each process execution
db13d8ac   Etienne Pallier   updates: go_idle ...
483
484
485
    #for (p,agent) in current_processes:
    for process in current_processes:
        p,agent,_ = process
31e31f3b   Etienne Pallier   pyros.py peut lan...
486
487
        print(f"************ Waiting for end of execution of agent {agent} ************")
        p.wait()
db13d8ac   Etienne Pallier   updates: go_idle ...
488
        process[2] = p.returncode
31e31f3b   Etienne Pallier   pyros.py peut lan...
489
490
491
492
493
494
495
496
        print(f"************ END of execution of agent {agent} ************")
        if p.returncode == 0:
            printFullTerm(Colors.GREEN, f"Process {agent} executed successfully")
            # self.addExecuted(self.current_command, command)
        else:
            printFullTerm(Colors.WARNING, f"Process {agent} execution failed")
            # self.addError(self.current_command, command)
    
db13d8ac   Etienne Pallier   updates: go_idle ...
497
498
499
500
501
    print()
    print()
    print("Synthesis of the results:")
    for process in current_processes:
        p,agent,returncode = process
db13d8ac   Etienne Pallier   updates: go_idle ...
502
503
504
505
506
507
508
        if returncode == 0:
            printFullTerm(Colors.GREEN, f"Process {agent} executed successfully")
            # self.addExecuted(self.current_command, command)
        else:
            printFullTerm(Colors.WARNING, f"Process {agent} execution failed")
            # self.addError(self.current_command, command)
        
31e31f3b   Etienne Pallier   pyros.py peut lan...
509
510
    #print("************ end of START() ************")
    # Only according to the last process status:
db13d8ac   Etienne Pallier   updates: go_idle ...
511
    #return True if p.returncode==0 else False
31e31f3b   Etienne Pallier   pyros.py peut lan...
512
    return True if p.returncode==0 else False
b2e9866d   Etienne Pallier   bugfix start_agen...
513
514


ddb06a2a   Etienne Pallier   new pyros2 launch...
515
516
517
@pyros_launcher.command(help="Kill an agent")
@click.argument('agent')
def stop(agent):
1f6376dd   Etienne Pallier   pyros2 refactor &...
518
    print("Running stop command")
f4fd20f9   Etienne Pallier   pyros.py script i...
519
    if not _check_agent(agent): return
ddb06a2a   Etienne Pallier   new pyros2 launch...
520
521
522



f4fd20f9   Etienne Pallier   pyros.py script i...
523
524
525
526
527
528
"""
********************************************************************************
******************** PRIVATE FUNCTIONS DEFINITION ******************************
********************************************************************************
"""

082ccda3   Etienne Pallier   pyros.py : enrich...
529
def _update_python_packages_from_requirements():
b280dbd0   Etienne Pallier   ./pyros update =>...
530
531
532
533
    # 1) try to upgrade pip
    res = execProcessFromVenv("-m pip install --upgrade pip")
    if not res: return False
    # 2) install only "not yet installed" python packages
f34862ce   Etienne Pallier   bugfix requirements
534
    res = execProcessFromVenv("-m pip install -r install/"+REQUIREMENTS)
082ccda3   Etienne Pallier   pyros.py : enrich...
535
536
    return res

f3d076fb   Etienne Pallier   Génération automa...
537
def _update_plantuml_diags():
ad6d274a   Etienne Pallier   Restructuration d...
538
    for dirpath, dirnames, files in os.walk(PYROS_DJANGO_BASE_DIR):
caabbdea   Etienne Pallier   plantUML diagrams...
539
540
        if os.path.basename(dirpath) == "doc":
            diagrams = glob.glob(dirpath+os.sep+"*.pu")
8787abc8   Etienne Pallier   ./pyros update ne...
541
542
            # For each diagram source file (.pu), generate the diagram PNG file (only if CHANGED)
            for diag in diagrams:
e9d89a42   Etienne Pallier   bugfix pyros update
543
544
545
                # if no diag.png or if diag.pu more recent than diag.png => re-generate diag.png
                diag_png = diag.split('.pu')[0] + '.png'
                if not os.path.isfile(diag_png) or ( os.path.getmtime(diag) > os.path.getmtime(diag_png) ) :
8787abc8   Etienne Pallier   ./pyros update ne...
546
547
548
                    res = execProcessFromVenv("-m plantuml "+diag)
                    if not res: return False
    return True
f3d076fb   Etienne Pallier   Génération automa...
549

f4fd20f9   Etienne Pallier   pyros.py script i...
550
def _migrate():
ad6d274a   Etienne Pallier   Restructuration d...
551
    change_dir(PYROS_DJANGO_BASE_DIR)
7d679ea0   Etienne Pallier   Commande EVAL ok
552
553
554
    # Migrate only migrations for the app "common"
    #res = execProcessFromVenv("manage.py migrate common")
    # Migrate all migrations for ALL apps
f4fd20f9   Etienne Pallier   pyros.py script i...
555
    res = execProcessFromVenv("manage.py migrate")
ad6d274a   Etienne Pallier   Restructuration d...
556
    change_dir("PREVIOUS")
f4fd20f9   Etienne Pallier   pyros.py script i...
557
558
559
    return res

def _makemigrations():
ad6d274a   Etienne Pallier   Restructuration d...
560
    change_dir(PYROS_DJANGO_BASE_DIR)
f4fd20f9   Etienne Pallier   pyros.py script i...
561
    #execProcessFromVenv(self.venv_bin + " manage.py makemigrations")
7d679ea0   Etienne Pallier   Commande EVAL ok
562
    #res = execProcessFromVenv("manage.py makemigrations common")
f4fd20f9   Etienne Pallier   pyros.py script i...
563
    res = execProcessFromVenv("manage.py makemigrations")
ad6d274a   Etienne Pallier   Restructuration d...
564
    change_dir("PREVIOUS")
f4fd20f9   Etienne Pallier   pyros.py script i...
565
566
567
568
    return res

#TODO: mettre la fixture en date naive (sans time zone)
def _loaddata():
ad6d274a   Etienne Pallier   Restructuration d...
569
    change_dir(PYROS_DJANGO_BASE_DIR)
a3015e31   Etienne Pallier   table des commandes
570
571
    #execProcessFromVenv(self.venv_bin + " manage.py loaddata misc" + os.sep + "fixtures" + os.sep + self.INIT_FIXTURE)
    res = execProcessFromVenv("manage.py loaddata misc" + os.sep + "fixtures" + os.sep + INIT_FIXTURE)
ad6d274a   Etienne Pallier   Restructuration d...
572
    change_dir("PREVIOUS")
f4fd20f9   Etienne Pallier   pyros.py script i...
573
574
    return res

77f1c546   Etienne Pallier   Ajouté options "t...
575

f3d076fb   Etienne Pallier   Génération automa...
576
def change_dir(path):
ad6d274a   Etienne Pallier   Restructuration d...
577
578
579
    global _previous_dir
    if path == "PREVIOUS": path=_previous_dir
    _previous_dir = os.getcwd()
f4fd20f9   Etienne Pallier   pyros.py script i...
580
581
582
583
584
585
586
587
    if DEBUG: print("Moving to : " + path)
    os.chdir(path)
    if DEBUG: print("Current directory : " + str(os.getcwd()))



def _check_agent(agent):
    # Check that agent exists
7d679ea0   Etienne Pallier   Commande EVAL ok
588
589
    if agent != "all" and agent not in AGENTS.keys() :
        # Check if multiple agents:
f4fd20f9   Etienne Pallier   pyros.py script i...
590
591
592
593
594
595
596
597
598
599
600
601
602
603
        print("This agent does not exist")
        print("Here is the allowed list of agents:")
        print("- all => will launch ALL agents")
        for agent_name in AGENTS.keys(): print('-',agent_name)
        return False
    return True



"""
********************************************************************************
********************************* main() FUNCTION ******************************
********************************************************************************
"""
ddb06a2a   Etienne Pallier   new pyros2 launch...
604
605
606
607
def main():
    pyros_launcher()
    
    
1f6376dd   Etienne Pallier   pyros2 refactor &...
608
# AVIRER
ddb06a2a   Etienne Pallier   new pyros2 launch...
609
610
611
612
613
614
615
616
617
618
#@click.command()
#@click.argument('start')
def oldmain():
    '''
    cli()
    return
    '''
    
    #fire.Fire(Commands)
    #return
efbcac9e   Etienne Pallier   1st version of NE...
619

b2e9866d   Etienne Pallier   bugfix start_agen...
620
621
622
623
624
625
626
627
628
629
630
    # if len(sys.argv) == 3 and sys.argv[1].startswith("simulator"): SIMULATOR_CONFIG_FILE = sys.argv[2]
    # print(sys.argv)
    """
    system = platform.system()
    columns = 100
    row = 1000
    disp = True
    """

    # Read command
    if len(sys.argv) <= 1:
a3015e31   Etienne Pallier   table des commandes
631
        die("You must give a command name")
b2e9866d   Etienne Pallier   bugfix start_agen...
632
633
    command = sys.argv[1]
    if not command in COMMANDS.keys():
a3015e31   Etienne Pallier   table des commandes
634
        die("This command does not exist")
b2e9866d   Etienne Pallier   bugfix start_agen...
635
636
637
638
639
640
    command_all_args = COMMANDS[command]

    # Read command args if should be
    command_arg = None
    if command_all_args:
        if len(sys.argv) <= 2:
a3015e31   Etienne Pallier   table des commandes
641
            die("This command should be given an argument")
b2e9866d   Etienne Pallier   bugfix start_agen...
642
643
        command_arg = sys.argv[2]
        if not command_arg in command_all_args:
a3015e31   Etienne Pallier   table des commandes
644
            die("This argument does not exist for command " + command)
b2e9866d   Etienne Pallier   bugfix start_agen...
645
646
647
648
649
650
651
652
653
654
655

    print("Executing command", command)
    if command_arg:
        print("with arg", command_arg)

    # command(command_arg)
    if command_arg:
        globals()[command](command_arg)
    else:
        globals()[command]()
    # sys.exit(pyros.exec())
ddb06a2a   Etienne Pallier   new pyros2 launch...
656
657
658
659



if __name__ == "__main__": main()