diff --git a/subiquity/controller.py b/subiquity/controller.py
index a3cb73f4..7f6d928d 100644
--- a/subiquity/controller.py
+++ b/subiquity/controller.py
@@ -28,6 +28,12 @@ class ControllerPolicyException(Exception):
class ControllerPolicy:
""" Expected contract for defining controllers
"""
+ def __init__(self, common):
+ self.ui = common['ui']
+ self.signal = common['signal']
+ self.opts = common['opts']
+ self.loop = common['loop']
+ self.prober = common['prober']
def register_signals(self):
""" Defines signals associated with controller from model """
diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py
index 9819de37..70a12c91 100644
--- a/subiquity/controllers/filesystem.py
+++ b/subiquity/controllers/filesystem.py
@@ -14,7 +14,6 @@
# along with this program. If not, see .
import logging
-import urwid
from subiquity.controller import ControllerPolicy
from subiquity.models import FilesystemModel
from subiquity.ui.views import (DiskPartitionView, AddPartitionView,
@@ -30,11 +29,9 @@ BIOS_GRUB_SIZE_BYTES = 2 * 1024 * 1024 # 2MiB
class FilesystemController(ControllerPolicy):
- def __init__(self, ui, signal, prober):
- self.ui = ui
- self.signal = signal
- self.prober = prober
- self.model = FilesystemModel(prober)
+ def __init__(self, common):
+ super().__init__(common)
+ self.model = FilesystemModel(self.prober)
def filesystem(self, reset=False):
# FIXME: Is this the best way to zero out this list for a reset?
@@ -45,7 +42,7 @@ class FilesystemController(ControllerPolicy):
title = "Filesystem setup"
footer = ("Select available disks to format and mount")
self.ui.set_header(title)
- self.ui.set_footer(footer)
+ self.ui.set_footer(footer, 30)
self.ui.set_body(FilesystemView(self.model,
self.signal))
diff --git a/subiquity/controllers/identity.py b/subiquity/controllers/identity.py
index 54842439..86762479 100644
--- a/subiquity/controllers/identity.py
+++ b/subiquity/controllers/identity.py
@@ -19,9 +19,8 @@ from subiquity.ui.views import IdentityView
class IdentityController(ControllerPolicy):
- def __init__(self, ui, signal):
- self.ui = ui
- self.signal = signal
+ def __init__(self, common):
+ super().__init__(common)
self.model = IdentityModel()
def identity(self):
diff --git a/subiquity/controllers/installpath.py b/subiquity/controllers/installpath.py
index 6c6e7274..c4e54e85 100644
--- a/subiquity/controllers/installpath.py
+++ b/subiquity/controllers/installpath.py
@@ -23,9 +23,8 @@ log = logging.getLogger('subiquity.controller.installpath')
class InstallpathController(ControllerPolicy):
- def __init__(self, ui, signal):
- self.ui = ui
- self.signal = signal
+ def __init__(self, common):
+ super().__init__(common)
self.model = InstallpathModel()
def installpath(self):
@@ -38,7 +37,7 @@ class InstallpathController(ControllerPolicy):
"navigate options")
self.ui.set_header(title, excerpt)
- self.ui.set_footer(footer)
+ self.ui.set_footer(footer, 10)
self.ui.set_body(InstallpathView(self.model, self.signal))
def install_ubuntu(self):
diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py
index 11a83184..e3dbc40c 100644
--- a/subiquity/controllers/installprogress.py
+++ b/subiquity/controllers/installprogress.py
@@ -24,10 +24,8 @@ log = logging.getLogger("subiquity.controller.installprogress")
class InstallProgressController(ControllerPolicy):
- def __init__(self, ui, signal, opts):
- self.ui = ui
- self.signal = signal
- self.opts = opts
+ def __init__(self, common):
+ super().__init__(common)
self.model = InstallProgressModel()
self.progress_output_w = ProgressOutput(self.signal, "Waiting...")
@@ -37,29 +35,31 @@ class InstallProgressController(ControllerPolicy):
@coroutine
def curtin_dispatch(self):
+ write_fd = self.loop.watch_pipe(self.install_progress_status)
if self.opts.dry_run:
log.debug("Install Progress: Curtin dispatch dry-run")
yield utils.run_command_async("cat /var/log/syslog",
- self.install_progress_status)
+ write_fd)
else:
try:
yield utils.run_command_async("/usr/local/bin/curtin_wrap.sh",
- self.install_progress_status)
+ write_fd)
except:
log.error("Problem with curtin dispatch run")
raise Exception("Problem with curtin dispatch run")
@coroutine
def initial_install(self):
+ write_fd = self.loop.watch_pipe(self.install_progress_status)
if self.opts.dry_run:
log.debug("Filesystem: this is a dry-run")
yield utils.run_command_async("cat /var/log/syslog",
- log.debug)
+ write_fd)
else:
log.debug("filesystem: this is the *real* thing")
yield utils.run_command_async(
"/usr/local/bin/curtin_wrap.sh",
- log.debug)
+ write_fd)
@coroutine
def show_progress(self):
@@ -71,13 +71,13 @@ class InstallProgressController(ControllerPolicy):
self.ui.set_footer(footer)
self.ui.set_body(ProgressView(self.signal, self.progress_output_w))
- if self.opts.dry_run:
- banner = [
- "**** DRY_RUN ****",
- "NOT calling:"
- "subprocess.check_call(/usr/local/bin/curtin_wrap.sh)"
- "",
- "",
- "Press (Q) to Quit."
- ]
- self.install_progress_status("\n".join(banner))
+ # if self.opts.dry_run:
+ # banner = [
+ # "**** DRY_RUN ****",
+ # "NOT calling:"
+ # "subprocess.check_call(/usr/local/bin/curtin_wrap.sh)"
+ # "",
+ # "",
+ # "Press (Q) to Quit."
+ # ]
+ # self.install_progress_status("\n".join(banner))
diff --git a/subiquity/controllers/network.py b/subiquity/controllers/network.py
index 2b4e2452..2efced38 100644
--- a/subiquity/controllers/network.py
+++ b/subiquity/controllers/network.py
@@ -20,10 +20,8 @@ from subiquity.ui.dummy import DummyView
class NetworkController(ControllerPolicy):
- def __init__(self, ui, signal, prober):
- self.ui = ui
- self.signal = signal
- self.prober = prober
+ def __init__(self, common):
+ super().__init__(common)
self.model = NetworkModel(self.prober)
def network(self):
@@ -33,7 +31,7 @@ class NetworkController(ControllerPolicy):
"sufficient access for updates.")
footer = ("Additional networking info here")
self.ui.set_header(title, excerpt)
- self.ui.set_footer(footer)
+ self.ui.set_footer(footer, 20)
self.ui.set_body(NetworkView(self.model, self.signal))
def set_default_route(self):
diff --git a/subiquity/controllers/welcome.py b/subiquity/controllers/welcome.py
index 884af952..9185d0f2 100644
--- a/subiquity/controllers/welcome.py
+++ b/subiquity/controllers/welcome.py
@@ -20,10 +20,9 @@ from subiquity.controller import ControllerPolicy
class WelcomeController(ControllerPolicy):
- def __init__(self, ui, signal):
- self.ui = ui
+ def __init__(self, common):
+ super().__init__(common)
self.model = WelcomeModel()
- self.signal = signal
def welcome(self):
title = "Wilkommen! Bienvenue! Welcome! Zdrastvutie! Welkom!"
diff --git a/subiquity/core.py b/subiquity/core.py
index 53453540..0fe5f546 100644
--- a/subiquity/core.py
+++ b/subiquity/core.py
@@ -17,18 +17,11 @@ import logging
import urwid
import urwid.curses_display
from tornado.ioloop import IOLoop
+from tornado.util import import_object
from subiquity.signals import Signal
from subiquity.palette import STYLES, STYLES_MONO
from subiquity.prober import Prober
-# Modes import ----------------------------------------------------------------
-from subiquity.controllers import (WelcomeController,
- InstallpathController,
- NetworkController,
- FilesystemController,
- IdentityController,
- InstallProgressController)
-
log = logging.getLogger('subiquity.core')
@@ -39,21 +32,21 @@ class CoreControllerError(Exception):
class Controller:
def __init__(self, ui, opts):
- self.ui = ui
- self.opts = opts
- self.signal = Signal()
- self.prober = Prober(self.opts)
- self.controllers = {
- "welcome": WelcomeController(self.ui, self.signal),
- "installpath": InstallpathController(self.ui, self.signal),
- "network": NetworkController(self.ui, self.signal, self.prober),
- "filesystem": FilesystemController(self.ui, self.signal,
- self.prober),
- "identity": IdentityController(self.ui, self.signal),
- "progress": InstallProgressController(self.ui, self.signal,
- self.opts)
+ self.common = {
+ "ui": ui,
+ "opts": opts,
+ "signal": Signal(),
+ "prober": Prober(opts),
+ "loop": None
+ }
+ self.controllers = {
+ "Welcome": None,
+ "Installpath": None,
+ "Network": None,
+ "Filesystem": None,
+ "Identity": None,
+ "InstallProgress": None,
}
- self._connect_base_signals()
def _connect_base_signals(self):
""" Connect signals used in the core controller
@@ -63,23 +56,23 @@ class Controller:
# Add quit signal
signals.append(('quit', self.exit))
signals.append(('refresh', self.redraw_screen))
- self.signal.connect_signals(signals)
+ self.common['signal'].connect_signals(signals)
# Registers signals from each controller
for controller, controller_class in self.controllers.items():
controller_class.register_signals()
- log.debug(self.signal)
+ log.debug(self.common['signal'])
# EventLoop -------------------------------------------------------------------
def redraw_screen(self):
if hasattr(self, 'loop'):
try:
- self.loop.draw_screen()
+ self.common['loop'].draw_screen()
except AssertionError as e:
log.critical("Redraw screen error: {}".format(e))
def set_alarm_in(self, interval, cb):
- self.loop.set_alarm_in(interval, cb)
+ self.common['loop'].set_alarm_in(interval, cb)
return
def update(self, *args, **kwds):
@@ -101,7 +94,7 @@ class Controller:
'unhandled_input': self.header_hotkeys,
'handle_mouse': False
}
- if self.opts.run_on_serial:
+ if self.common['opts'].run_on_serial:
palette = STYLES_MONO
additional_opts['screen'] = urwid.curses_display.Screen()
else:
@@ -109,15 +102,22 @@ class Controller:
additional_opts['screen'].reset_default_terminal_palette()
evl = urwid.TornadoEventLoop(IOLoop())
- self.loop = urwid.MainLoop(
- self.ui, palette, event_loop=evl, **additional_opts)
- log.debug("Running event loop: {}".format(self.loop.event_loop))
+ self.common['loop'] = urwid.MainLoop(
+ self.common['ui'], palette, event_loop=evl, **additional_opts)
+ log.debug("Running event loop: {}".format(
+ self.common['loop'].event_loop))
try:
self.set_alarm_in(0.05, self.welcome)
- # self.install_progress_fd = self.loop.watch_pipe(
- # self.install_progress_status)
- self.loop.run()
+ for k in self.controllers.keys():
+ log.debug("Importing controller: {}".format(k))
+ klass = import_object(
+ "subiquity.controllers.{}Controller".format(
+ k))
+ self.controllers[k] = klass(self.common)
+
+ self._connect_base_signals()
+ self.common['loop'].run()
except:
log.exception("Exception in controller.run():")
raise
@@ -126,4 +126,4 @@ class Controller:
#
# Starts the initial UI view.
def welcome(self, *args, **kwargs):
- self.controllers['welcome'].welcome()
+ self.controllers['Welcome'].welcome()
diff --git a/subiquity/palette.py b/subiquity/palette.py
index 62a43fd9..bcc2e357 100644
--- a/subiquity/palette.py
+++ b/subiquity/palette.py
@@ -56,7 +56,11 @@ STYLES = [
('string_input', '', '', '',
Palette.black, Palette.light_gray),
('string_input focus', '', '', '',
- Palette.white, Palette.dark_gray)
+ Palette.white, Palette.dark_gray),
+ ('progress_incomplete', '', '', '',
+ Palette.white, Palette.dark_magenta),
+ ('progress_complete', '', '', '',
+ Palette.white, Palette.light_magenta)
]
@@ -75,4 +79,12 @@ STYLES_MONO = [('frame_header', Palette.white, Palette.black,
('button', Palette.white, Palette.black,
'', '', ''),
('button focus', Palette.white, Palette.black,
- '', '', '')]
+ '', '', ''),
+ ('string_input', '', '', '',
+ Palette.white, ''),
+ ('string_input focus', '', '', '',
+ Palette.white, ''),
+ ('progress_incomplete', '', '', '',
+ '', Palette.black),
+ ('progress_complete', '', '', '',
+ '', Palette.white)]
diff --git a/subiquity/ui/anchors.py b/subiquity/ui/anchors.py
index 3f27dd2e..363d6fcf 100644
--- a/subiquity/ui/anchors.py
+++ b/subiquity/ui/anchors.py
@@ -13,7 +13,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from urwid import WidgetWrap, Pile, Text
+from urwid import WidgetWrap, Pile, Text, ProgressBar
+from collections import deque
from subiquity.ui.utils import Padding, Color
from subiquity.ui.lists import SimpleList
@@ -47,10 +48,19 @@ class Footer(WidgetWrap):
"""
- def __init__(self, message=""):
+ def __init__(self, message="", completion=0):
message_widget = Padding.center_79(Color.body(Text(message)))
- status = Pile([Padding.line_break(""), message_widget])
- super().__init__(status)
+ progress_bar = Padding.center_60(
+ ProgressBar(normal='progress_incomplete',
+ complete='progress_complete',
+ current=completion, done=100))
+ status = deque([
+ Padding.line_break(""),
+ message_widget
+ ])
+ if completion > 0:
+ status.appendleft(progress_bar)
+ super().__init__(Pile(status))
class Body(WidgetWrap):
diff --git a/subiquity/ui/frame.py b/subiquity/ui/frame.py
index 8e2d0289..21680f07 100644
--- a/subiquity/ui/frame.py
+++ b/subiquity/ui/frame.py
@@ -40,8 +40,8 @@ class SubiquityUI(WidgetWrap):
def set_header(self, title=None, excerpt=None):
self.frame.header = Header(title, excerpt)
- def set_footer(self, message):
- self.frame.footer = Footer(message)
+ def set_footer(self, message, completion=0):
+ self.frame.footer = Footer(message, completion)
def set_body(self, widget):
self.frame.body = widget
diff --git a/subiquity/utils.py b/subiquity/utils.py
index 22cfc6cf..075bf9fb 100644
--- a/subiquity/utils.py
+++ b/subiquity/utils.py
@@ -13,11 +13,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-import errno
import subprocess
-import os
-import codecs
-import pty
from subiquity.async import Async
import shlex
import logging
@@ -35,60 +31,7 @@ def run_command(cmd, streaming_callback=None):
if isinstance(cmd, str):
cmd = shlex.split(cmd)
log.debug("Running command: {}".format(cmd))
- stdoutm, stdouts = pty.openpty()
- proc = subprocess.Popen(cmd,
- stdout=stdouts,
- stderr=subprocess.PIPE)
- os.close(stdouts)
- decoder = codecs.getincrementaldecoder('utf-8')()
-
- def last_ten_lines(s):
- chunk = s[-1500:]
- lines = chunk.splitlines(True)
- return ''.join(lines[-10:]).replace('\r', '')
-
- decoded_output = ""
- try:
- while proc.poll() is None:
- try:
- b = os.read(stdoutm, 512)
- except OSError as e:
- if e.errno != errno.EIO:
- raise
- break
- else:
- final = False
- if not b:
- final = True
- decoded_chars = decoder.decode(b, final)
- if decoded_chars is None:
- continue
-
- decoded_output += decoded_chars
- if streaming_callback:
- ls = last_ten_lines(decoded_output)
-
- streaming_callback(ls)
- if final:
- break
- finally:
- os.close(stdoutm)
- if proc.poll() is None:
- proc.kill()
- proc.wait()
-
- errors = [l.decode('utf-8') for l in proc.stderr.readlines()]
- if streaming_callback:
- streaming_callback(last_ten_lines(decoded_output))
-
- errors = ''.join(errors)
-
- if proc.returncode == 0:
- return decoded_output.strip()
- else:
- log.debug("Error with command: "
- "[Output] '{}' [Error] '{}'".format(
- decoded_output.strip(),
- errors.strip()))
- raise Exception("Problem running command: [Error] '{}'".format(
- errors.strip()))
+ proc = subprocess.Popen(cmd, close_fds=True,
+ stdout=streaming_callback)
+ proc.kill()
+ # streaming_callback.close()