console_conf: start initial console-conf tree with a custom controller story
And a custom Welcome screen to boot; to show the overriding of settings. Signed-off-by: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
This commit is contained in:
parent
e1970b6477
commit
848ccabc7d
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import logging
|
||||
import signal
|
||||
from subiquitycore.log import setup_logger, LOGFILE
|
||||
from subiquitycore import __version__ as VERSION
|
||||
from console_conf.core import Controller as ConsoleConf
|
||||
from subiquitycore.core import CoreControllerError
|
||||
from subiquitycore.ui.frame import SubiquityUI
|
||||
from subiquitycore.utils import environment_check
|
||||
|
||||
|
||||
def parse_options(argv):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='console-conf - Pre-Ownership Configuration for Ubuntu Core',
|
||||
prog='console-conf')
|
||||
parser.add_argument('--dry-run', action='store_true',
|
||||
dest='dry_run',
|
||||
help='menu-only, do not call installer function')
|
||||
# XXX Defaults to firstboot mode unless one runs subiquity --install.
|
||||
parser.add_argument('--install', action='store_false',
|
||||
dest='firstboot', default=True,
|
||||
help='run installer in firstboot mode')
|
||||
parser.add_argument('--serial', action='store_true',
|
||||
dest='run_on_serial',
|
||||
help='Run the installer over serial console.')
|
||||
parser.add_argument('--machine-config', metavar='CONFIG',
|
||||
dest='machine_config',
|
||||
help="Don't Probe. Use probe data file")
|
||||
parser.add_argument('--uefi', action='store_true',
|
||||
dest='uefi',
|
||||
help='run in uefi support mode')
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def control_c_handler(signum, frame):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
opts = parse_options(sys.argv[1:])
|
||||
setup_logger()
|
||||
logger = logging.getLogger('console-conf')
|
||||
logger.info("Starting console-conf v{}".format(VERSION))
|
||||
logger.info("Arguments passed: {}".format(sys.argv))
|
||||
|
||||
signal.signal(signal.SIGINT, control_c_handler)
|
||||
|
||||
env_ok = environment_check()
|
||||
if env_ok is False and not opts.dry_run:
|
||||
print('Failed environment check. '
|
||||
'Check {} for errors.'.format(LOGFILE))
|
||||
return 1
|
||||
|
||||
ui = SubiquityUI()
|
||||
|
||||
try:
|
||||
interface = ConsoleConf(ui, opts)
|
||||
except CoreControllerError as e:
|
||||
logger.exception('Failed to load ConsoleConf interface')
|
||||
print(e)
|
||||
return 1
|
||||
|
||||
interface.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
""" console-conf controllers """
|
||||
|
||||
from .welcome import WelcomeController
|
||||
from subiquitycore.controllers.network import NetworkController
|
||||
from subiquitycore.controllers.login import LoginController
|
||||
from subiquitycore.controllers.identity import IdentityController
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
from subiquitycore.controller import ControllerPolicy
|
||||
from subiquitycore.models import IdentityModel
|
||||
from subiquitycore.ui.views import IdentityView, LoginView
|
||||
|
||||
log = logging.getLogger('subiquitycore.controllers.identity')
|
||||
|
||||
class IdentityController(ControllerPolicy):
|
||||
def __init__(self, common):
|
||||
super().__init__(common)
|
||||
self.model = IdentityModel(self.opts)
|
||||
|
||||
def identity(self):
|
||||
title = "Profile setup"
|
||||
excerpt = ("Input your username and password to log in to the system.")
|
||||
footer = ""
|
||||
self.ui.set_header(title, excerpt)
|
||||
self.ui.set_footer(footer, 40)
|
||||
self.ui.set_body(IdentityView(self.model, self.signal, self.opts))
|
||||
|
||||
|
||||
def login(self):
|
||||
log.debug("Identity login view")
|
||||
title = ("Configuration Complete")
|
||||
footer = ("View configured user and device access methods")
|
||||
self.ui.set_header(title)
|
||||
self.ui.set_footer(footer)
|
||||
|
||||
net_model = self.controllers['Network'].model
|
||||
configured_ifaces = net_model.get_configured_interfaces()
|
||||
login_view = LoginView(self.model,
|
||||
self.signal,
|
||||
self.model.user,
|
||||
configured_ifaces)
|
||||
|
||||
self.ui.set_body(login_view)
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from subiquitycore.ui.views import LoginView
|
||||
from subiquitycore.models import LoginModel
|
||||
from subiquitycore.controller import ControllerPolicy
|
||||
|
||||
|
||||
class LoginController(ControllerPolicy):
|
||||
def __init__(self, common):
|
||||
super().__init__(common)
|
||||
self.model = LoginModel()
|
||||
|
||||
def login(self):
|
||||
title = "Configuration Complete"
|
||||
excerpt = "Your device is now configured. Login details below."
|
||||
self.ui.set_header(title, excerpt)
|
||||
view = LoginView(self.model, self.signal, self.model.user)
|
||||
self.ui.set_body(view)
|
|
@ -0,0 +1,94 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
from subiquitycore.controller import ControllerPolicy
|
||||
from subiquitycore.models import NetworkModel
|
||||
from subiquitycore.ui.views import (NetworkView,
|
||||
NetworkSetDefaultRouteView,
|
||||
NetworkBondInterfacesView,
|
||||
NetworkConfigureInterfaceView,
|
||||
NetworkConfigureIPv4InterfaceView)
|
||||
from subiquitycore.ui.dummy import DummyView
|
||||
|
||||
from subiquitycore.curtin import curtin_write_network_actions
|
||||
from subiquitycore.curtin import curtin_apply_networking
|
||||
|
||||
log = logging.getLogger("subiquitycore.controller.network")
|
||||
|
||||
|
||||
class NetworkController(ControllerPolicy):
|
||||
def __init__(self, common):
|
||||
super().__init__(common)
|
||||
self.model = NetworkModel(self.prober, self.opts)
|
||||
|
||||
def network(self):
|
||||
title = "Network connections"
|
||||
excerpt = ("Configure at least the main interface this server will "
|
||||
"use to talk to other machines, and preferably provide "
|
||||
"sufficient access for updates.")
|
||||
footer = ("Additional networking info here")
|
||||
self.ui.set_header(title, excerpt)
|
||||
self.ui.set_footer(footer, 20)
|
||||
self.ui.set_body(NetworkView(self.model, self.signal))
|
||||
|
||||
def network_finish(self, actions):
|
||||
try:
|
||||
curtin_write_network_actions(actions)
|
||||
except (PermissionError, FileNotFoundError):
|
||||
log.exception('Failed to obtain write permission')
|
||||
self.signal.emit_signal('filesystem:error',
|
||||
'curtin_write_network_actions')
|
||||
return None
|
||||
|
||||
curtin_apply_networking(actions, dryrun=self.opts.dry_run)
|
||||
|
||||
# switch to identity view
|
||||
self.signal.emit_signal('menu:identity:main')
|
||||
|
||||
def set_default_route(self):
|
||||
self.ui.set_header("Default route")
|
||||
self.ui.set_body(NetworkSetDefaultRouteView(self.model,
|
||||
self.signal))
|
||||
|
||||
def bond_interfaces(self):
|
||||
self.ui.set_header("Bond interfaces")
|
||||
self.ui.set_body(NetworkBondInterfacesView(self.model,
|
||||
self.signal))
|
||||
|
||||
def network_configure_interface(self, iface):
|
||||
self.ui.set_header("Network interface {}".format(iface))
|
||||
self.ui.set_body(NetworkConfigureInterfaceView(self.model,
|
||||
self.signal,
|
||||
iface))
|
||||
|
||||
def network_configure_ipv4_interface(self, iface):
|
||||
self.model.prev_signal = ('Back to configure interface menu',
|
||||
'network:configure-interface-menu',
|
||||
'network_configure_interface')
|
||||
self.ui.set_header("Network interface {} manual IPv4 "
|
||||
"configuration".format(iface))
|
||||
self.ui.set_body(NetworkConfigureIPv4InterfaceView(self.model,
|
||||
self.signal,
|
||||
iface))
|
||||
|
||||
def network_configure_ipv6_interface(self, iface):
|
||||
self.model.prev_signal = ('Back to configure interface menu',
|
||||
'network:configure-interface-menu',
|
||||
'network_configure_interface')
|
||||
self.ui.set_body(DummyView(self.signal))
|
||||
|
||||
def install_network_driver(self):
|
||||
self.ui.set_body(DummyView(self.signal))
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from console_conf.ui.views import WelcomeView
|
||||
from subiquitycore.models import WelcomeModel
|
||||
from subiquitycore.controllers.welcome import WelcomeController as WelcomeControllerBase
|
||||
|
||||
|
||||
class WelcomeController(WelcomeControllerBase):
|
||||
def welcome(self):
|
||||
title = " Welcome!"
|
||||
excerpt = "Please choose your preferred language"
|
||||
footer = ("Use UP, DOWN arrow keys, and ENTER, to "
|
||||
"select your language.")
|
||||
self.ui.set_header(title, excerpt)
|
||||
self.ui.set_footer(footer)
|
||||
view = WelcomeView(self.model, self.signal)
|
||||
self.ui.set_body(view)
|
|
@ -0,0 +1,94 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
import urwid
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.util import import_object
|
||||
from subiquitycore.signals import Signal
|
||||
from subiquitycore.palette import STYLES, STYLES_MONO
|
||||
from subiquitycore.prober import Prober, ProberException
|
||||
|
||||
from subiquitycore.core import CoreControllerError, Controller as ControllerBase
|
||||
|
||||
log = logging.getLogger('console_conf.core')
|
||||
|
||||
|
||||
class CoreControllerError(Exception):
|
||||
""" Basecontroller exception """
|
||||
pass
|
||||
|
||||
|
||||
class Controller(ControllerBase):
|
||||
def __init__(self, ui, opts):
|
||||
try:
|
||||
prober = Prober(opts)
|
||||
except ProberException as e:
|
||||
err = "Prober init failed: {}".format(e)
|
||||
log.exception(err)
|
||||
raise CoreControllerError(err)
|
||||
|
||||
self.common = {
|
||||
"ui": ui,
|
||||
"opts": opts,
|
||||
"signal": Signal(),
|
||||
"prober": prober,
|
||||
"loop": None
|
||||
}
|
||||
self.controllers = {
|
||||
"Welcome": None,
|
||||
"Network": None,
|
||||
"Identity": None,
|
||||
"Login": None,
|
||||
}
|
||||
self.common['controllers'] = self.controllers
|
||||
|
||||
def run(self):
|
||||
if not hasattr(self, 'loop'):
|
||||
palette = STYLES
|
||||
additional_opts = {
|
||||
'screen': urwid.raw_display.Screen(),
|
||||
'unhandled_input': self.header_hotkeys,
|
||||
'handle_mouse': False
|
||||
}
|
||||
if self.common['opts'].run_on_serial:
|
||||
palette = STYLES_MONO
|
||||
else:
|
||||
additional_opts['screen'].set_terminal_properties(colors=256)
|
||||
additional_opts['screen'].reset_default_terminal_palette()
|
||||
|
||||
evl = urwid.TornadoEventLoop(IOLoop())
|
||||
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)
|
||||
for k in self.controllers.keys():
|
||||
log.debug("Importing controller: {}".format(k))
|
||||
klass = import_object(
|
||||
"subiquitycore.controllers.{}Controller".format(
|
||||
k))
|
||||
klass = import_object(
|
||||
"console_conf.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
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
""" Subiquity UI Components """
|
|
@ -0,0 +1,18 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
""" ConsoleConf UI Views """
|
||||
|
||||
from .welcome import WelcomeView
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2015 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
""" Welcome
|
||||
|
||||
Welcome provides user with language selection
|
||||
|
||||
"""
|
||||
import logging
|
||||
from urwid import (ListBox, Pile, BoxAdapter)
|
||||
from subiquitycore.ui.lists import SimpleList
|
||||
from subiquitycore.ui.buttons import menu_btn, ok_btn, cancel_btn
|
||||
from subiquitycore.ui.utils import Padding, Color
|
||||
from subiquitycore.view import ViewPolicy
|
||||
from subiquitycore.ui.views.welcome import CoreWelcomeView
|
||||
|
||||
log = logging.getLogger("console_conf.views.welcome")
|
||||
|
||||
|
||||
class WelcomeView(CoreWelcomeView):
|
||||
def confirm(self, result):
|
||||
self.model.selected_language = result.label
|
||||
log.debug('calling network')
|
||||
self.signal.emit_signal('menu:network:main')
|
|
@ -30,6 +30,6 @@ from .network_configure_ipv4_interface import NetworkConfigureIPv4InterfaceView
|
|||
from .network_bond_interfaces import NetworkBondInterfacesView # NOQA
|
||||
from .installpath import InstallpathView # NOQA
|
||||
from .installprogress import ProgressView # NOQA
|
||||
from .welcome import WelcomeView # NOQA
|
||||
from .welcome import CoreWelcomeView as WelcomeView # NOQA
|
||||
from .identity import IdentityView # NOQA
|
||||
from .login import LoginView # NOQA
|
||||
|
|
|
@ -28,7 +28,7 @@ from subiquitycore.view import ViewPolicy
|
|||
log = logging.getLogger("subiquitycore.views.welcome")
|
||||
|
||||
|
||||
class WelcomeView(ViewPolicy):
|
||||
class CoreWelcomeView(ViewPolicy):
|
||||
def __init__(self, model, signal):
|
||||
self.model = model
|
||||
self.signal = signal
|
||||
|
|
Loading…
Reference in New Issue