commit | author | age
|
7169f1
|
1 |
import argparse |
SP |
2 |
import paramiko |
|
3 |
from . import Remote |
61d2e7
|
4 |
from . import trisurf |
7169f1
|
5 |
import socket |
SP |
6 |
import sys |
9f5ff5
|
7 |
import tabulate |
a3f6b7
|
8 |
import subprocess,re |
dfa2df
|
9 |
import psutil |
7169f1
|
10 |
#import http.server |
SP |
11 |
#import socketserver |
61d2e7
|
12 |
if sys.version_info>=(3,0): |
SP |
13 |
from urllib.parse import urlparse |
|
14 |
from . import WebTrisurf |
|
15 |
else: |
|
16 |
from urlparse import urlparse |
c89a71
|
17 |
from vtk import * |
7169f1
|
18 |
#import io |
5f4aea
|
19 |
|
SP |
20 |
|
7169f1
|
21 |
|
SP |
22 |
#Color definitions for terminal |
|
23 |
class bcolors: |
|
24 |
HEADER = '\033[95m' |
|
25 |
OKBLUE = '\033[94m' |
|
26 |
OKGREEN = '\033[92m' |
|
27 |
WARNING = '\033[93m' |
|
28 |
FAIL = '\033[91m' |
|
29 |
ENDC = '\033[0m' |
|
30 |
BOLD = '\033[1m' |
|
31 |
UNDERLINE = '\033[4m' |
|
32 |
|
|
33 |
#parses Command Line Arguments and returns the list of parsed values |
|
34 |
def ParseCLIArguments(arguments): |
|
35 |
parser = argparse.ArgumentParser(description='Manages (start, stop, status) multiple simulation processes of trisurf according to the configuration file.') |
|
36 |
parser.add_argument('proc_no', metavar='PROC_NO', nargs='*', |
|
37 |
help='process number at host. If hostname is not specified, localhost is assumed. If no processes are specified all processes on all hosts are assumed.') |
|
38 |
action_group=parser.add_mutually_exclusive_group(required=True) |
|
39 |
action_group.add_argument('-c','--comment',nargs=1, help='append comment to current comment') |
|
40 |
action_group.add_argument('--delete-comment', help='delete comment',action='store_true') |
|
41 |
action_group.add_argument('-k','--kill','--stop','--suspend', help='stop/kill the process', action='store_true') |
|
42 |
action_group.add_argument('-r','--run','--start','--continue', help='start/continue process', action='store_true') |
|
43 |
action_group.add_argument('-s','--status',help='print status of the processes',action='store_true') |
|
44 |
action_group.add_argument('-v','--version', help='print version information and exit', action='store_true') |
|
45 |
action_group.add_argument('--web-server', type=int,metavar="PORT", nargs=1, help='EXPERIMENTAL: starts web server and never exist.') |
c89a71
|
46 |
action_group.add_argument('-p','--preview',help='preview last VTU shape',action='store_true') |
7169f1
|
47 |
parser.add_argument('--force', help='if dangerous operation (killing all the processes) is requested, this flag is required to execute the operation. Otherwise, the request will be ignored.', action="store_true") |
SP |
48 |
parser.add_argument('-H', '--host', nargs=1, help='specifies which host is itended for the operation. Defauts to localhost for all operations except --status and --version, where all configured hosts are assumed.') |
|
49 |
parser.add_argument('--html', help='Generate HTML output', action="store_true") |
|
50 |
parser.add_argument('-n', nargs='+', metavar='PROC_NO', type=int, help='OBSOLETE. Specifies process numbers.') |
|
51 |
parser.add_argument('-R','--raw',help='print status and the rest of the information in raw format', action="store_true") |
|
52 |
parser.add_argument('-x','--local-only',help='do not attempt to contact remote hosts. Run all operations only on local machine',action='store_true') |
|
53 |
args = parser.parse_args(arguments) |
|
54 |
return args |
|
55 |
|
|
56 |
|
|
57 |
#gets version of trisurf currently running |
a3f6b7
|
58 |
def getTrisurfVersion(): |
SP |
59 |
p = subprocess.Popen('trisurf --version', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
|
60 |
lines=p.stdout.readlines() |
|
61 |
version=re.findall(r'[0-9a-f]{7}(?:-dirty)?', lines[0].decode('ascii')) |
|
62 |
p.wait() |
|
63 |
if(len(version)): |
|
64 |
return version[0] |
|
65 |
else: |
|
66 |
return "unknown version" |
|
67 |
|
9f5ff5
|
68 |
|
7169f1
|
69 |
|
SP |
70 |
def copyConfigAndConnect(hosts): |
|
71 |
print("Connecting to remote hosts and copying config files, tapes and snapshots") |
|
72 |
for host in hosts: |
|
73 |
if(host['name'] !=socket.gethostname()): #if I am not the computer named in host name |
|
74 |
try: |
|
75 |
username=host['username'] |
|
76 |
except: |
|
77 |
username=os.getusername() #default username is current user user's name |
|
78 |
try: |
|
79 |
port=host['port'] |
|
80 |
except: |
|
81 |
port=22 #default ssh port |
|
82 |
rm=Remote.Connection(hostname=host['address'],username=username, port=port) |
|
83 |
rm.connect() |
|
84 |
rm.send_file(__file__,'remote_control.py') |
|
85 |
for run in host['runs']: |
|
86 |
try: |
|
87 |
rm.send_file(run.tapeFile,run.tapeFile) |
|
88 |
except: |
|
89 |
pass |
|
90 |
try: |
|
91 |
rm.send_file(run.snapshotFile,run.snapshotFile) |
|
92 |
except: |
|
93 |
pass |
|
94 |
host['_conn']= rm |
|
95 |
# we are connected to all hosts... |
|
96 |
return hosts |
|
97 |
|
|
98 |
|
|
99 |
|
|
100 |
def getTargetRunIdxList(args): |
|
101 |
target_runs=list(map(int,args['proc_no'])) |
|
102 |
if len(target_runs)==0: |
|
103 |
#check if obsolete -n flags have numbers |
|
104 |
target_runs=args['n'] |
|
105 |
if target_runs==None: |
|
106 |
return None |
|
107 |
target_runs=list(set(target_runs)) |
|
108 |
return target_runs |
|
109 |
|
|
110 |
|
|
111 |
|
|
112 |
def status_processes(args,host): |
|
113 |
target_runs=getTargetRunIdxList(args) |
|
114 |
if target_runs==None: |
|
115 |
target_runs=list(range(1,len(host['runs'])+1)) |
|
116 |
report=[] |
|
117 |
for i in target_runs: |
|
118 |
line=host['runs'][i-1].getStatistics() |
|
119 |
line.insert(0,i) |
|
120 |
report.append(line) |
|
121 |
if(args['raw']): |
|
122 |
print(report) |
|
123 |
else: |
|
124 |
if(args['html']): |
|
125 |
tablefmt='html' |
9f5ff5
|
126 |
else: |
7169f1
|
127 |
tablefmt='fancy_grid' |
SP |
128 |
print(tabulate.tabulate(report,headers=["Run no.", "Run start time", "ETA", "Status", "PID", "Path", "Comment"], tablefmt=tablefmt)) |
|
129 |
return |
|
130 |
|
|
131 |
def run_processes(args,host): |
|
132 |
target_runs=getTargetRunIdxList(args) |
|
133 |
if target_runs==None: |
|
134 |
target_runs=list(range(1,len(host['runs'])+1)) |
|
135 |
for i in target_runs: |
|
136 |
host['runs'][i-1].start() |
|
137 |
return |
|
138 |
|
|
139 |
def kill_processes(args,host): |
|
140 |
target_runs=getTargetRunIdxList(args) |
|
141 |
if target_runs==None: |
|
142 |
if args['force']==True: |
|
143 |
target_runs=list(range(1,len(host['runs'])+1)) |
|
144 |
else: |
|
145 |
print("Not stopping all processes on the host. Run with --force flag if you are really sure to stop all simulations") |
|
146 |
return |
|
147 |
for i in target_runs: |
|
148 |
host['runs'][i-1].stop() |
|
149 |
return |
|
150 |
|
|
151 |
def comment_processes(args,host): |
|
152 |
target_runs=getTargetRunIdxList(args) |
|
153 |
if target_runs==None: |
|
154 |
target_runs=list(range(1,len(host['runs'])+1)) |
|
155 |
for i in target_runs: |
|
156 |
host['runs'][i-1].writeComment(args['comment'][0],'a') |
|
157 |
print("Comment added") |
|
158 |
return |
|
159 |
|
|
160 |
def delete_comments(args,host): |
|
161 |
target_runs=getTargetRunIdxList(args) |
|
162 |
if target_runs==None: |
|
163 |
if args['force']==True: |
|
164 |
target_runs=list(range(1,len(host['runs'])+1)) |
|
165 |
else: |
|
166 |
print("Not deleting comments on all posts on the host. Run with --force flag if you are really sure to delete all comments") |
|
167 |
return |
|
168 |
for i in target_runs: |
|
169 |
host['runs'][i-1].writeComment("") |
|
170 |
print("Comment deleted") |
|
171 |
return |
|
172 |
|
|
173 |
|
|
174 |
def start_web_server(args,host): |
|
175 |
print('Server listening on port {}'.format(args['web_server'][0])) |
61d2e7
|
176 |
if sys.version_info>=(3,0): |
SP |
177 |
WebTrisurf.WebServer(port=args['web_server'][0]) |
|
178 |
else: |
|
179 |
print("Cannot start WebServer in python 2.7") |
7169f1
|
180 |
exit(0) |
SP |
181 |
|
|
182 |
def perform_action(args,host): |
|
183 |
#find which flags have been used and act upon them. -r -s -k -v -c --delete-comment are mutually exclusive, so only one of them is active |
|
184 |
if args['run']: |
|
185 |
run_processes(args,host) |
|
186 |
elif args['kill']: |
|
187 |
kill_processes(args,host) |
|
188 |
elif args['status']: |
|
189 |
status_processes(args,host) |
|
190 |
elif args['comment']!= None: |
|
191 |
comment_processes(args,host) |
|
192 |
elif args['delete_comment']: |
|
193 |
delete_comments(args,host) |
|
194 |
elif args['web_server']!=None: |
|
195 |
start_web_server(args,host) |
c89a71
|
196 |
elif args['preview']: |
SP |
197 |
preview_vtu(args,host) |
7169f1
|
198 |
else: #version requested |
SP |
199 |
print(getTrisurfVersion()) |
|
200 |
return |
|
201 |
|
|
202 |
|
c89a71
|
203 |
|
SP |
204 |
def preview_vtu(args,host): |
|
205 |
#only for localhost at the moment |
|
206 |
filename=host['runs'][0].getLastVTU() |
|
207 |
print(filename) |
|
208 |
if sys.version_info>=(3,0): |
|
209 |
print("Preview works only with python 2.7") |
|
210 |
exit(1) |
|
211 |
if host['name'] == socket.gethostname(): |
|
212 |
target_runs=getTargetRunIdxList(args) |
|
213 |
#if target_runs==None: |
|
214 |
# target_runs=list(range(1,len(host['runs'])+1)) |
|
215 |
#for i in target_runs: |
|
216 |
# host['runs'][i-1].start() |
|
217 |
reader=vtkXMLUnstructuredGridReader() |
|
218 |
reader.SetFileName(filename) |
|
219 |
reader.Update() # Needed because of GetScalarRange |
|
220 |
output = reader.GetOutput() |
|
221 |
scalar_range = output.GetScalarRange() |
|
222 |
|
|
223 |
# Create the mapper that corresponds the objects of the vtk file |
|
224 |
# into graphics elements |
|
225 |
mapper = vtkDataSetMapper() |
|
226 |
mapper.SetInput(output) |
|
227 |
mapper.SetScalarRange(scalar_range) |
|
228 |
|
|
229 |
# Create the Actor |
|
230 |
actor = vtkActor() |
|
231 |
actor.SetMapper(mapper) |
|
232 |
|
|
233 |
# Create the Renderer |
|
234 |
renderer = vtkRenderer() |
|
235 |
renderer.AddActor(actor) |
|
236 |
renderer.SetBackground(0, 0, 0) # Set background to white |
|
237 |
|
|
238 |
# Create the RendererWindow |
|
239 |
renderer_window = vtkRenderWindow() |
|
240 |
renderer_window.AddRenderer(renderer) |
|
241 |
|
|
242 |
# Create the RendererWindowInteractor and display the vtk_file |
|
243 |
interactor = vtkRenderWindowInteractor() |
|
244 |
interactor.SetRenderWindow(renderer_window) |
|
245 |
interactor.Initialize() |
|
246 |
interactor.Start() |
|
247 |
|
|
248 |
|
7169f1
|
249 |
|
SP |
250 |
def getListOfHostConfigurationByHostname(hosts,host): |
|
251 |
rhost=[] |
|
252 |
for chost in hosts: |
|
253 |
if chost['name'] in host: |
|
254 |
rhost.append(chost) |
|
255 |
return rhost |
9f5ff5
|
256 |
|
SP |
257 |
|
|
258 |
|
7169f1
|
259 |
def start(hosts,argv=sys.argv[1:]): |
SP |
260 |
args=vars(ParseCLIArguments(argv)) |
|
261 |
#print(vars(args)) |
|
262 |
|
|
263 |
#Backward compatibility... If running just on localmode, the host specification is unnecessary. Check if only Runs are specified |
|
264 |
try: |
|
265 |
test_host=hosts[0]['name'] |
|
266 |
except: |
|
267 |
print("Network mode disabled. Old syntax detected.") |
|
268 |
host={'name':socket.gethostname(),'address':'127.0.0.1', 'runs':hosts} |
|
269 |
perform_action(args,host) |
|
270 |
exit(0) |
|
271 |
|
|
272 |
|
|
273 |
#find the host at which the action is attended |
|
274 |
if args['host']==None: |
|
275 |
if(args['status']==False and args['version']==False): |
|
276 |
hosts=getListOfHostConfigurationByHostname(hosts,socket.gethostname()) |
|
277 |
else: |
|
278 |
hosts=getListOfHostConfigurationByHostname(hosts,args['host']) |
|
279 |
if len(hosts)==0: |
|
280 |
print ('Hostname "{}" does not exist in configuration file. Please check the spelling'.format(args['host'][0])) |
|
281 |
exit(1) |
|
282 |
|
|
283 |
if not args['local_only']: |
|
284 |
hosts=copyConfigAndConnect(hosts) |
|
285 |
|
|
286 |
#do local stuff: |
|
287 |
for host in hosts: |
|
288 |
if host['name'] == socket.gethostname(): |
|
289 |
if(args['html']): |
|
290 |
print("Host <font color='orange'>"+host['name']+"</font> reports the following:") |
|
291 |
else: |
|
292 |
print("Host "+bcolors.WARNING+host['name']+bcolors.ENDC+" reports the following:") |
|
293 |
perform_action(args,host) |
|
294 |
elif not args['local_only']: |
|
295 |
output=host['_conn'].execute('python3 ./remote_control.py -x '+" ".join(argv)) |
|
296 |
for line in output: |
|
297 |
print(line.replace('\n','')) |
|
298 |
|
|
299 |
|
|
300 |
if not args['local_only']: |
|
301 |
print("Closing connections to remote hosts") |
|
302 |
for host in hosts: |
|
303 |
if(host['name'] !=socket.gethostname()): |
|
304 |
host['_conn'].disconnect() |
9f5ff5
|
305 |
|
SP |
306 |
|
|
307 |
|