Skip to content

Commit 549eb95

Browse files
committed
feat: integrate with RaspiBlitz
1 parent d9e0545 commit 549eb95

36 files changed

+2250
-2551
lines changed

images/utils/launcher/__init__.py

Lines changed: 154 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import logging
22
import shlex
33
import traceback
4-
import os.path
4+
from threading import Event
55

6-
from launcher.config import Config, ConfigLoader
7-
from launcher.shell import Shell
8-
from launcher.node import NodeManager, NodeNotFound
9-
from launcher.utils import ParallelExecutionError, ArgumentError
10-
11-
from launcher.check_wallets import Action as CheckWalletsAction
126
from launcher.close_other_utils import Action as CloseOtherUtilsAction
13-
from launcher.auto_unlock import Action as AutoUnlockAction
14-
from launcher.warm_up import Action as WarmUpAction
15-
from launcher.errors import FatalError, ConfigError, ConfigErrorScope
7+
from launcher.config import Config
8+
from launcher.errors import FatalError, ConfigError, ConfigErrorScope, NoWaiting, ParallelError
9+
from launcher.node import NodeManager, ServiceNotFound, ContainerNotFound
10+
from launcher.utils import ArgumentError
11+
import docker.errors
12+
import os
1613

14+
logger = logging.getLogger(__name__)
1715

1816
HELP = """\
1917
Xucli shortcut commands
@@ -91,165 +89,153 @@
9189
--inbound [inbound_balance] deposit from boltz (btc/ltc)
9290
boltzcli <chain> withdraw
9391
<amount> <address> withdraw from boltz channel
94-
9592
"""
9693

97-
98-
def init_logging():
99-
fmt = "%(asctime)s.%(msecs)03d %(levelname)5s %(process)d --- [%(threadName)-15s] %(name)-30s: %(message)s"
100-
datefmt = "%Y-%m-%d %H:%M:%S"
101-
if os.path.exists("/mnt/hostfs/tmp"):
102-
logfile = "/mnt/hostfs/tmp/xud-docker.log"
103-
else:
104-
logfile = "xud-docker.log"
105-
106-
logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO, filename=logfile, filemode="w")
107-
108-
level_config = {
109-
"launcher": logging.DEBUG,
110-
}
111-
112-
for logger, level in level_config.items():
113-
logging.getLogger(logger).setLevel(level)
114-
115-
116-
init_logging()
94+
REPORT = """Please click on https://github.com/ExchangeUnion/xud/issues/new?assignees=kilrau&labels=bug&template=bug-\
95+
report.md&title=Short%2C+concise+description+of+the+bug, describe your issue, drag and drop the file "{network}.log" \
96+
which is located in "{logs_dir}" into your browser window and submit your issue."""
11797

11898

11999
class XudEnv:
120-
def __init__(self, config, shell):
121-
self.logger = logging.getLogger("launcher.XudEnv")
122-
100+
def __init__(self, config: Config):
123101
self.config = config
124-
self.shell = shell
125-
126-
self.node_manager = NodeManager(config, shell)
127-
128-
def delegate_cmd_to_xucli(self, cmd):
129-
self.node_manager.get_node("xud").cli(cmd, self.shell)
130-
131-
def command_report(self):
132-
logs_dir = f"{self.config.home_dir}/{self.config.network}/logs"
133-
print(f"""Please click on https://github.com/ExchangeUnion/xud/issues/\
134-
new?assignees=kilrau&labels=bug&template=bug-report.md&title=Short%2C+concise+\
135-
description+of+the+bug, describe your issue, drag and drop the file "{self.config.network}\
136-
.log" which is located in "{logs_dir}" into your browser window and submit \
137-
your issue.""")
138-
139-
def handle_command(self, cmd):
140-
try:
141-
args = shlex.split(cmd)
142-
arg0 = args[0]
102+
self.node_manager = NodeManager(config)
103+
104+
def handle_command(self, cmd: str) -> None:
105+
args = shlex.split(cmd)
106+
arg0 = args[0]
107+
args = args[1:]
108+
109+
if arg0 == "help":
110+
print(HELP)
111+
elif arg0 == "status":
112+
self.node_manager.status()
113+
elif arg0 == "report":
114+
print(REPORT.format(self.config.network, self.config.logs_dir))
115+
elif arg0 == "logs":
116+
self.node_manager.cmd_logs.execute(*args)
117+
elif arg0 == "start":
118+
self.node_manager.cmd_start.execute(*args)
119+
elif arg0 == "stop":
120+
self.node_manager.cmd_stop.execute(*args)
121+
elif arg0 == "restart":
122+
self.node_manager.cmd_restart.execute(*args)
123+
elif arg0 == "_create":
124+
self.node_manager.cmd_create.execute(*args)
125+
elif arg0 == "rm":
126+
self.node_manager.cmd_remove.execute(*args)
127+
elif arg0 == "down":
128+
self.node_manager.down()
129+
elif arg0 == "up":
130+
self.node_manager.up()
131+
elif arg0 == "bitcoin-cli":
132+
bitcoind = self.node_manager.get_service("bitcoind")
133+
bitcoind.cli(" ".join(args))
134+
elif arg0 == "litecoin-cli":
135+
litecoind = self.node_manager.get_service("litecoind")
136+
litecoind.cli(" ".join(args))
137+
elif arg0 == "lndbtc-lncli":
138+
lndbtc = self.node_manager.get_service("lndbtc")
139+
lndbtc.cli(" ".join(args))
140+
elif arg0 == "lndltc-lncli":
141+
lndltc = self.node_manager.get_service("lndltc")
142+
lndltc.cli(" ".join(args))
143+
elif arg0 == "geth":
144+
geth = self.node_manager.get_service("geth")
145+
geth.cli(" ".join(args))
146+
elif arg0 == "xucli":
147+
xud = self.node_manager.get_service("xud")
148+
xud.cli(" ".join(args))
149+
elif arg0 == "boltzcli":
150+
boltz = self.node_manager.get_service("boltz")
151+
boltz.cli(" ".join(args))
152+
elif arg0 == "deposit":
153+
boltz = self.node_manager.get_service("boltz")
154+
if len(args) == 0:
155+
print("Missing chain")
156+
chain = args[0]
143157
args = args[1:]
144-
if arg0 == "status":
145-
self.node_manager.status()
146-
elif arg0 == "report":
147-
self.command_report()
148-
elif arg0 == "logs":
149-
self.node_manager.logs(*args)
150-
elif arg0 == "start":
151-
self.node_manager.start(*args)
152-
elif arg0 == "stop":
153-
self.node_manager.stop(*args)
154-
elif arg0 == "restart":
155-
self.node_manager.restart(*args)
156-
elif arg0 == "down":
157-
self.node_manager.down()
158-
elif arg0 == "up":
159-
self.node_manager.up()
160-
elif arg0 == "btcctl":
161-
self.node_manager.cli("btcd", *args)
162-
elif arg0 == "ltcctl":
163-
self.node_manager.cli("ltcd", *args)
164-
elif arg0 == "bitcoin-cli":
165-
self.node_manager.cli("bitcoind", *args)
166-
elif arg0 == "litecoin-cli":
167-
self.node_manager.cli("litecoind", *args)
168-
elif arg0 == "lndbtc-lncli":
169-
self.node_manager.cli("lndbtc", *args)
170-
elif arg0 == "lndltc-lncli":
171-
self.node_manager.cli("lndltc", *args)
172-
elif arg0 == "geth":
173-
self.node_manager.cli("geth", *args)
174-
elif arg0 == "xucli":
175-
self.node_manager.cli("xud", *args)
176-
elif arg0 == "boltzcli":
177-
self.node_manager.cli("boltz", *args)
178-
elif arg0 == "deposit":
179-
if len(args) == 0:
180-
print("Missing chain")
181-
chain = args[0]
182-
args = args[1:]
183-
if chain == "btc":
184-
self.node_manager.cli("boltz", "btc", "deposit", *args)
185-
elif chain == "ltc":
186-
self.node_manager.cli("boltz", "ltc", "deposit", *args)
187-
else:
188-
self.node_manager.cli("xud", "walletdeposit", chain, *args)
189-
elif arg0 == "withdraw":
190-
if len(args) == 0:
191-
print("Missing chain")
192-
chain = args[0]
193-
args = args[1:]
194-
if chain == "btc":
195-
self.node_manager.cli("boltz", "btc", "withdraw", *args)
196-
elif chain == "ltc":
197-
self.node_manager.cli("boltz", "ltc", "withdraw", *args)
198-
else:
199-
self.node_manager.cli("xud", "walletwithdraw", chain, *args)
200-
elif arg0 == "help":
201-
print(HELP)
158+
if chain == "btc":
159+
boltz.cli("btc deposit " + " ".join(args))
160+
elif chain == "ltc":
161+
boltz.cli("ltc deposit " + " ".join(args))
202162
else:
203-
self.delegate_cmd_to_xucli(cmd)
204-
205-
except NodeNotFound as e:
206-
if str(e) == "boltz" and self.config.network == "simnet":
207-
print("Not available on simnet")
208-
return
209-
210-
print(f"Node not found: {e}")
211-
except ArgumentError as e:
212-
print(e.usage)
213-
print(f"error: {e}")
214-
215-
def check_wallets(self):
216-
CheckWalletsAction(self.node_manager).execute()
217-
218-
def wait_for_channels(self):
219-
# TODO wait for channels
220-
pass
221-
222-
def auto_unlock(self):
223-
AutoUnlockAction(self.node_manager).execute()
163+
xud = self.node_manager.get_service("xud")
164+
xud.cli("walletdeposit %s %s" % (chain, " ".join(args)))
165+
elif arg0 == "withdraw":
166+
boltz = self.node_manager.get_service("boltz")
167+
if len(args) == 0:
168+
print("Missing chain")
169+
chain = args[0]
170+
args = args[1:]
171+
if chain == "btc":
172+
boltz.cli("btc withdraw " + " ".join(args))
173+
elif chain == "ltc":
174+
boltz.cli("ltc withdraw " + " ".join(args))
175+
else:
176+
xud = self.node_manager.get_service("xud")
177+
xud.cli("walletwithdraw %s %s" % (chain, " ".join(args)))
178+
else:
179+
xud = self.node_manager.get_service("xud")
180+
xud.cli(cmd)
224181

225182
def close_other_utils(self):
226-
CloseOtherUtilsAction(self.config.network, self.shell).execute()
227-
228-
def warm_up(self):
229-
WarmUpAction(self.node_manager).execute()
230-
231-
def pre_start(self):
232-
self.warm_up()
233-
self.check_wallets()
183+
CloseOtherUtilsAction(self.config.network).execute()
234184

235-
if self.config.network == "simnet":
236-
self.wait_for_channels()
185+
def pre_shell(self):
186+
print("\n🏃 Warming up...\n")
237187

238-
self.auto_unlock()
188+
xud = self.node_manager.get_service("xud")
189+
stop = Event()
190+
try:
191+
# FIXME pty signal only works in main thread
192+
xud.ensure_ready(stop)
193+
except (KeyboardInterrupt, NoWaiting):
194+
stop.set()
195+
raise
239196

240197
self.close_other_utils()
241198

242199
def start(self):
243-
self.logger.info("Start %s", self.config.network)
244-
245-
up_env = self.node_manager.update()
246-
247-
if up_env:
248-
self.node_manager.up()
249-
self.pre_start()
250-
251-
self.logger.info("Start shell")
252-
self.shell.start(f"{self.config.network} > ", self.handle_command)
200+
logger.info("Start %s", self.config.network)
201+
202+
self.node_manager.update()
203+
204+
self.node_manager.up()
205+
206+
self.pre_shell()
207+
208+
logger.info("Start shell")
209+
banner_file = os.path.dirname(__file__) + "/banner.txt"
210+
with open(banner_file) as f:
211+
print(f.read(), end="", flush=True)
212+
prompt = f"{self.config.network} > "
213+
while True:
214+
try:
215+
cmd = input(prompt)
216+
cmd = cmd.strip()
217+
if cmd == "":
218+
continue
219+
if cmd == "exit":
220+
break
221+
try:
222+
self.handle_command(cmd)
223+
except KeyboardInterrupt:
224+
pass
225+
except ServiceNotFound as e:
226+
print("Service not found: %s" % e)
227+
except ContainerNotFound as e:
228+
print("Service not running: %s" % e)
229+
except docker.errors.APIError as e:
230+
print(e)
231+
except ArgumentError as e:
232+
print(e.usage)
233+
print(f"Error: {e}")
234+
except:
235+
logger.exception("[Shell] Failed to execute command: %s", cmd)
236+
traceback.print_exc()
237+
except KeyboardInterrupt:
238+
print()
253239

254240

255241
def print_config_error_cause(e: ConfigError) -> None:
@@ -262,40 +248,25 @@ def print_config_error_cause(e: ConfigError) -> None:
262248

263249

264250
class Launcher:
265-
def __init__(self):
266-
self.logger = logging.getLogger("launcher.Launcher")
267-
self.logfile = None
268-
269251
def launch(self):
270-
shell = Shell()
271252
config = None
272253
try:
273-
config = Config(ConfigLoader())
274-
shell.set_network_dir(config.network_dir) # will create shell history file in network_dir
275-
env = XudEnv(config, shell)
254+
config = Config()
255+
env = XudEnv(config)
276256
env.start()
277257
except KeyboardInterrupt:
278258
print()
279-
except ConfigError as e:
280-
if e.scope == ConfigErrorScope.COMMAND_LINE_ARGS:
281-
print("Failed to parse command-line arguments, exiting.")
282-
print_config_error_cause(e)
283-
elif e.scope == ConfigErrorScope.GENERAL_CONF:
284-
print("Failed to parse config file {}, exiting.".format(e.conf_file))
285-
print_config_error_cause(e)
286-
elif e.scope == ConfigErrorScope.NETWORK_CONF:
287-
print("Failed to parse config file {}, exiting.".format(e.conf_file))
288-
print_config_error_cause(e)
259+
exit(1)
260+
except NoWaiting:
261+
exit(1)
289262
except FatalError as e:
290-
if config and config.logfile:
291-
print("{}. For more details, see {}".format(e, config.logfile))
292-
else:
293-
traceback.print_exc()
294-
except ParallelExecutionError:
295-
pass
296-
except Exception: # exclude system exceptions like SystemExit
297-
self.logger.exception("Unexpected exception during launching")
298-
traceback.print_exc()
299-
finally:
300-
shell.stop()
301-
263+
msg = "💀 %s." % str(e)
264+
if config:
265+
msg += " For more details, see %s" % config.host_logfile
266+
print(msg)
267+
exit(1)
268+
except ParallelError:
269+
if config:
270+
msg = "For more details, see %s" % config.host_logfile
271+
print(msg)
272+
exit(1)

0 commit comments

Comments
 (0)