Rahix / tbot

Automation/Testing tool for Embedded Linux Development
https://tbot.tools
GNU General Public License v3.0
91 stars 21 forks source link

Board Linux shall have a way to be soft reseted (`reboot`) #26

Open niecore opened 4 years ago

niecore commented 4 years ago

It would be great to trigger a soft reset with reboot for some testcases. Unfortunately exec0 reboot will not terminate because the prompt will not be shown again. The softreset function could handle the relogin as well.

Rahix commented 4 years ago

My current pattern for soft-resets is this:

with contexlib.ExitStack() as cx:
    b = cx.enter_context(tbot.acquire_board(lh))
    lnx = cx.enter_context(tbot.acquire_linux(b))

    ...

    ch = lnx.open_channel("systemctl", "reboot")
    # Stuff the channel back into the board-machine ...
    b.ch = ch

    # Reinitialize the linux-machine which will wait for the reboot to
    # reach the login-prompt again.
    lnx = cx.enter_context(tbot.acquire_linux(b))

    ...

I know, this isn't the pretties way to do it and I was also thinking that maybe it would be useful to have a way for doing soft-resets with a single method call. But I'm not really sure how to model this without making a lot of assumptions about the user, so we'll have to see ...

For now, this explicit re-initialization is the pattern I would suggest you use.

avylove commented 2 years ago

I wasn't able to get the example to work. It would hang once Linux would shutdown, but wouldn't power cycle. This does seem to work for me

  re_poweroff = re.compile(b'reboot: Power down\s{0,10}')
  shutdown_cmd = ("sudo", "systemctl", "poweroff")

  with tbot.ctx.request(tbot.role.BoardLinux) as serial:
      with serial.run(*shutdown_cmd) as shutdown:
          shutdown.read_until_prompt(prompt=re_poweroff, timeout=60)
          shutdown._pre_terminate()
          shutdown._proxy_alive = False

I would prefer not to use private attributes, but I couldn't get it to work cleanly otherwise. I tried running serial.ch.read_until_prompt(), but it just hung like before.

Any advice on achieving something similar without accessing private attributes?

Rahix commented 2 years ago

I am not 100% sure what you are trying to do... You want to first power-off the system and then have tbot power-cycle it, is that correct? In that case, try something like this:

with tbot.ctx.request(tbot.role.BoardLinux, exclusive=True) as lnx:
    # Do something before reboot
    lnx.exec0("uname", "-a")

    # Power-down the system
    ch = lnx.open_channel("systemctl", "poweroff")
    ev = tbot.log.EventIO(["powerdown"], "System powering down...")
    with ch.with_stream(ev):
        ev.prefix = "   <> "
        ch.expect(["reboot: Power down", "reboot: System halted"])

with tbot.ctx.request(tbot.role.BoardLinux, reset=True) as lnx:
    # Do something after reboot
    lnx.exec0("uname", "-a")

FWIW, this issue was originally about performing a soft-reset without power-cycling the system. For that, things work a bit differently...

avylove commented 2 years ago

Thanks! Using Channel.expect() is much cleaner.

For my use case, either a reboot or a shutdown will work. I just need the OS to shutdown cleanly and not just power cycle. The issue I had with the reboot is it just hung. Perhaps it also needs Channel.expect()?

avylove commented 2 years ago

Turns out I do need to reboot. I tried to implement what you have in https://github.com/Rahix/tbot/issues/26#issuecomment-663580867. It works if I'm just running additional commands on the serial console within that same context block, but, if I call other code after this that requests the LinuxBoard role and uses the channel, I'll get a ChannelTakenException. I'm guessing the global context is sharing the original machine object and not the second one?

franzflasch commented 1 year ago

I would also need to do a reboot without a power cylcle in between. How can this be done cleanly?