Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import os 

2import random 

3import time 

4import sys 

5import psutil 

6from logging import debug, warn, error 

7 

8from cement import App, Controller, ex 

9 

10from gutools.controllers.arguments import * 

11from gutools.tools import update_context, _call, prepare_call, expandpath 

12from gutools.tools import parse_uri 

13from gutools.system import get_fingerprint, test_alive 

14from gutools.session import USession, UProcess, codenamize 

15from gutools.bash_completer import CompletableController, find_process_by_cmdline 

16 

17class SessionController(CompletableController): 

18 handled_uris = ['session', 'exec'] 

19 class Meta: 

20 label = 'session' 

21 stacked_on = 'base' 

22 stacked_type = 'nested' 

23 description = 'session manager' 

24 arguments = [] 

25 hide = False 

26 

27 @classmethod 

28 def can_handle(cls, uri): 

29 uri_ = parse_uri(uri) 

30 return uri_['scheme'] in cls.handled_uris 

31 

32 def __init__(self, *args, **kw): 

33 super().__init__(*args, **kw) 

34 

35 # add custom expansor from some commands 

36 self._add_expander('stop', 'pid', self._exp_from_db) 

37 self._add_expander('stop', 'name', self._exp_from_db) 

38 

39 

40 @ex(arguments=[ARGS, PATH], help='Init a session') 

41 def init(self): 

42 """Create a session in curent folder.""" 

43 pargs = self.app.pargs 

44 data = { 

45 'foo' : 'bar', 

46 } 

47 pargs.path = pargs.path or (pargs.args and pargs.args[0]) or expandpath(os.path.curdir) 

48 update_context(data, self.app.pargs) 

49 session = _call(USession, **data) 

50 session.init() 

51 

52 update_context(data, session) 

53 self.app.render(data, 'session.init.jinja2') 

54 

55 @ex(arguments=[ARGS, PATH], help='activate a session') 

56 def activate(self): 

57 """Activate session""" 

58 pargs = self.app.pargs 

59 data = { 

60 'foo' : 'bar', 

61 } 

62 pargs.path = pargs.path or (pargs.args and pargs.args[0]) or expandpath(os.path.curdir) 

63 update_context(data, self.app.pargs) 

64 session = _call(USession, **data) 

65 

66 self.app.render(data, 'default.jinja2') 

67 

68 session.activate() 

69 

70 @ex(arguments=[ARGS, PATH], help='deactivate a session') 

71 def deactivate(self): 

72 """Deactivate running session""" 

73 pargs = self.app.pargs 

74 data = { 

75 'foo' : 'bar', 

76 } 

77 pargs.path = pargs.path or (pargs.args and pargs.args[0]) or expandpath(os.path.curdir) 

78 update_context(data, pargs) 

79 session = _call(USession, **data) 

80 # TODO: isolate session daemon task in Controllers or Session 

81 # TODO: it seems has more sense in Session as gather all info in fodler 

82 self.app.render(data, 'default.jinja2') 

83 

84 session.deactivate() 

85 

86 

87 

88 @ex() 

89 def status(self): 

90 """Show session status.""" 

91 data = { 

92 'foo' : 'bar', 

93 } 

94 self.app.render(data, 'session.status.jinja2') 

95 

96 @ex() 

97 def list(self): 

98 """Show session status.""" 

99 pargs = self.app.pargs 

100 data = { 

101 'path' : '.', 

102 } 

103 update_context(data, self.app.pargs) 

104 

105 session = _call(USession, **data) 

106 results = list(session.workspace.find_process()) 

107 data['results'] = results 

108 

109 self.app.render(data, 'session.list.jinja2') 

110 

111 foo = 1 

112 

113 @ex() 

114 def exit(self): 

115 """Show session status.""" 

116 data = { 

117 'foo' : 'bar', 

118 } 

119 self.app.render(data, 'session.exit.jinja2') 

120 

121 

122 @ex(arguments=[URI, NAME, ALIAS, RESTART, SHUTDOWN]) 

123 def start(self): 

124 """Start a subnode task based on uri. 

125 Example: 

126 

127 $ atlas session start --uri "exec://localhost/home/agp/Jts/tws?username=myuser&password=mypassword" --alias master.tws --restart 1 --shutdown 1 

128 $ atlas session start --uri=atlas://localhost:9090 

129 $ atlas session start --uri=ib://localhost:7496?account=DU123456 

130 

131 You need to enclose in "" if any parameter contains characteres like: 

132 

133 &, 

134 

135 atlas session start --uri "ib://sync:9090?master&account=1" --alias 

136 """ 

137 pargs = self.app.pargs 

138 data = { 

139 'path' : '.', 

140 } 

141 update_context(data, pargs) 

142 

143 session = _call(USession, **data) 

144 self.app.render(data, 'session.start.jinja2') 

145 if self.can_handle(pargs.uri): 

146 uri = parse_uri(pargs.uri) 

147 if uri['scheme'] in ('exec', ): 

148 session._launch_process(**data) 

149 else: 

150 assert uri['scheme'] in ('session', ) 

151 pass 

152 else: 

153 self._delegate_controller_dispatch() 

154 

155 print('-End-2-') 

156 

157 @ex(arguments=[ARGS, URI, NAME]) 

158 def stop(self): 

159 """Stop a subnode task. 

160 Example: 

161 

162 atlas stop --uri=atlas://localhost:9090 

163 atlas stop --name=atlas://localhost:9090 

164 

165 """ 

166 pargs = self.app.pargs 

167 data = { 

168 'path' : '.', 

169 } 

170 pargs.name = pargs.name or pargs.args[0] 

171 update_context(data, self.app.pargs) 

172 

173 session = _call(USession, **data) 

174 self.app.render(data, 'default.jinja2') 

175 

176 context = session.stop(**data) 

177 

178 @ex(arguments=[ARGS, URI, PID, NAME, ALIAS, TIMEOUT, CONDITION]) 

179 def wait(self): 

180 """Wait until a condition over a process is true. 

181 Example: 

182 

183 atlas session wait --alias master.tws --condition "len(fp['listen'])>0 --timeout 60" 

184 

185 """ 

186 pargs = self.app.pargs 

187 data = { 

188 'path': '.', 

189 } 

190 update_context(data, pargs) 

191 session = _call(USession, **data) 

192 

193 timeout = pargs.timeout or 0 

194 t1 = time.time() + timeout 

195 

196 while timeout <= 0 or time.time() < t1: 

197 try: 

198 score, proc, state, fp = session.locate_process(**data) 

199 update_context(data, fp=fp, proc=proc, state=state) 

200 

201 result = eval(pargs.condition, data, data) 

202 if result: 

203 break 

204 except Exception as why: 

205 print(f"{RED}{why}{RESET}") 

206 time.sleep(1) 

207 else: 

208 raise TimeoutError(f"waiting for {pargs.condition}") 

209 

210 foo = 1 

211 

212 @ex(arguments=[ARGS, PATH], help='deactivate a session') 

213 def random_process(self): 

214 """Deactivate running session""" 

215 pargs = self.app.pargs 

216 data = { 

217 'foo' : 'bar', 

218 } 

219 pargs.path = pargs.path or (pargs.args and pargs.args[0]) or expandpath(os.path.curdir) 

220 update_context(data, self.app.pargs) 

221 session = _call(USession, **data) 

222 

223 n = random.randint(100, 2000) 

224 uri = f'session://random?n={n}' 

225 self.app.render(data, 'default.jinja2') 

226 

227 session.start( 

228 target=session._demo_task, 

229 uri=uri, 

230 n=n) 

231 

232 

233 

234 

235 def _delegate_controller_dispatch(self): 

236 pargs = self.app.pargs 

237 for c in self._available_session_controllers(): 

238 print(c) 

239 if c.can_handle(pargs.uri): 

240 funcname = sys._getframe(1).f_code.co_name 

241 func = getattr(c, funcname) 

242 func() 

243 foo = 1 

244 

245 def _exp_from_db(self, attr): 

246 "Expand attrs from session database" 

247 candidates = set([]) 

248 

249 path = '.' 

250 session = _call(USession, path=path) 

251 candidates = set([]) 

252 for process in session.workspace.find_process(): 

253 if process.pid is None: 

254 continue 

255 value = getattr(process, attr, '') 

256 if value.startswith(partial): 

257 candidates.add(value.lstrip(prefix)) 

258 

259 return candidates 

260 

261 def _available_session_controllers(self): 

262 for c in self.app.controller._controllers: 

263 if isinstance(c, self.__class__) and c != self: 

264 yield c 

265 

266 # @ex(arguments=[NAME]) 

267 # def launch_process(self): 

268 # """Launch a previous captured process""" 

269 # pargs = self.app.pargs 

270 # data = { 

271 # 'path' : '.', 

272 # } 

273 # update_context(data, self.app.pargs) 

274 

275 # session = _call(USession, **data) 

276 

277 # result, proc, fp = session.launch_process(name=pargs.name) 

278 

279 # update_context(data, fp, result=result, proc=proc) 

280 # self.app.render(data, 'session.launch-process.jinja2') 

281 

282 @ex(arguments=[PID, URI, CMDLINE, ALIAS, STATE, RESTART, SHUTDOWN]) 

283 def attach(self): 

284 pargs = self.app.pargs 

285 data = { 

286 'path' : '.', 

287 } 

288 update_context(data, self.app.pargs) 

289 if pargs.pid: 

290 assert not pargs.uri 

291 assert not pargs.cmdline 

292 proc = psutil.Process(pargs.pid) 

293 update_context(data, proc=proc) 

294 

295 elif pargs.cmdline: 

296 assert not pargs.uri 

297 assert not pargs.pid 

298 proc = find_process_by_cmdline(pargs.cmdline) 

299 lproc = len(proc) 

300 if lproc > 1: 

301 print(f"too many matching processes ({lproc})") 

302 elif lproc < 1: 

303 print(f"process not found") 

304 else: 

305 proc = proc.pop() 

306 update_context(data, proc=proc) 

307 

308 elif pargs.uri: 

309 assert not pargs.pid 

310 assert not pargs.cmdline 

311 data['cmdline'] = sys.argv 

312 

313 session = _call(USession, **data) 

314 info = _call(session.attach, **data) 

315 update_context(data, info) 

316 # self.app.render(data, 'session.attach-process.jinja2') 

317 

318 foo = 1 

319 

320 

321 @ex(arguments=[PID, CMDLINE, ALIAS, LABEL]) 

322 def fooo(self): 

323 pargs = self.app.pargs 

324 data = { 

325 'path' : '.', 

326 } 

327 import uuid 

328 update_context(data, self.app.pargs) 

329 node = uuid.getnode() 

330 foo = 1 

331 

332 

333class SessionDependenceController(CompletableController): 

334 class Meta: 

335 label = 'dependence' 

336 stacked_on = 'session' 

337 stacked_type = 'nested' 

338 description = 'session dependence manager' 

339 arguments = [] 

340 hide = False 

341 

342 @ex(arguments=[PARENT, CHILD, ]) 

343 def add(self): 

344 """Add a process dependence within session 

345 Example: 

346 

347 atlas session dependence add --parent foo --child bar 

348 """ 

349 pargs = self.app.pargs 

350 data = { 

351 'path' : '.', 

352 } 

353 pargs.path = expandpath(os.path.curdir) 

354 update_context(data, self.app.pargs) 

355 session = _call(USession, **data) 

356 

357 session.add_dependence(pargs.parent, pargs.child) 

358 

359 self.app.render(data, 'default.jinja2') 

360 

361 

362 @ex(arguments=[PARENT, CHILD, ]) 

363 def remove(self): 

364 """Remove a process dependence within session 

365 Example: 

366 

367 atlas session dependence remove --parent foo --child bar 

368 """ 

369 pargs = self.app.pargs 

370 data = { 

371 'path' : '.', 

372 } 

373 pargs.path = expandpath(os.path.curdir) 

374 update_context(data, self.app.pargs) 

375 session = _call(USession, **data) 

376 

377 session.remove_dependence(pargs.parent, pargs.child) 

378 

379 self.app.render(data, 'default.jinja2')