Merge branch 'mwhudson/demo'

This commit is contained in:
Mathieu Trudel-Lapierre 2016-07-28 20:25:17 -04:00
commit d3c0bb95d7
11 changed files with 190 additions and 25 deletions

View File

@ -18,7 +18,7 @@ import argparse
import sys import sys
import logging import logging
import signal import signal
from subiquitycore.log import setup_logger, LOGFILE from subiquitycore.log import setup_logger
from subiquitycore import __version__ as VERSION from subiquitycore import __version__ as VERSION
from console_conf.core import ConsoleConf from console_conf.core import ConsoleConf
from subiquitycore.core import ApplicationError from subiquitycore.core import ApplicationError
@ -70,10 +70,14 @@ def parse_options(argv):
def control_c_handler(signum, frame): def control_c_handler(signum, frame):
sys.exit(1) sys.exit(1)
LOGDIR = "/writable/.subiquity"
def main(): def main():
opts = parse_options(sys.argv[1:]) opts = parse_options(sys.argv[1:])
setup_logger() global LOGDIR
if opts.dry_run:
LOGDIR = ".subiquity"
LOGFILE = setup_logger(dir=LOGDIR)
logger = logging.getLogger('console_conf') logger = logging.getLogger('console_conf')
logger.info("Starting console-conf v{}".format(VERSION)) logger.info("Starting console-conf v{}".format(VERSION))
logger.info("Arguments passed: {}".format(sys.argv)) logger.info("Arguments passed: {}".format(sys.argv))

View File

@ -16,8 +16,32 @@
from subiquitycore.controllers.identity import BaseIdentityController from subiquitycore.controllers.identity import BaseIdentityController
from console_conf.ui.views import IdentityView from console_conf.ui.views import IdentityView, LoginView
class IdentityController(BaseIdentityController): class IdentityController(BaseIdentityController):
identity_view = IdentityView identity_view = IdentityView
def identity(self):
title = "Profile setup"
excerpt = "Enter an email address from your account in the store."
footer = ""
self.ui.set_header(title, excerpt)
self.ui.set_footer(footer, 40)
self.ui.set_body(self.identity_view(self.model, self.signal, self.opts, self.loop))
def login(self):
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.opts,
self.model,
self.signal,
self.model.user,
configured_ifaces)
self.ui.set_body(login_view)

View File

@ -16,4 +16,5 @@
""" ConsoleConf UI Views """ """ ConsoleConf UI Views """
from .identity import IdentityView # NOQA from .identity import IdentityView # NOQA
from .login import LoginView # NOQA
from .welcome import WelcomeView # NOQA from .welcome import WelcomeView # NOQA

View File

@ -14,19 +14,130 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
import pwd
from subiquitycore.ui.views.identity import BaseIdentityView from urwid import (Pile, Columns, Text, ListBox)
from subiquitycore.user import create_user from subiquitycore.ui.buttons import done_btn, cancel_btn
from subiquitycore.ui.interactive import EmailEditor
from subiquitycore.ui.utils import Padding, Color
from subiquitycore.utils import run_command
from subiquitycore.view import BaseView
log = logging.getLogger("console_conf.views.identity") log = logging.getLogger("console_conf.views.identity")
class IdentityView(BaseIdentityView): '''
def create_user(self, result): +---------------------------------------------------+
try: | |
create_user( | Enter the email address of the account in the |
result, dryrun=self.opts.dry_run, extra_args=['--extrausers']) | store |
except PermissionError: | |
# XXX do something here | +-------------------------+ |
log.exception('Failed to create user.') | Email address: | | |
return None | +-------------------------+ |
| |
| |
| +--------+ |
| | Done | |
| +--------+ |
| | Cancel | |
| +--------+ |
| |
+---------------------------------------------------+
'''
class IdentityView(BaseView):
def __init__(self, model, signal, opts, loop):
self.model = model
self.signal = signal
self.opts = opts
self.loop = loop
self.items = []
self.email = EmailEditor(caption="")
self.error = Text("", align="center")
self.progress = Text("", align="center")
body = [
Padding.center_90(self._build_model_inputs()),
Padding.line_break(""),
Padding.center_90(Color.info_error(self.error)),
Padding.center_90(self.progress),
Padding.line_break(""),
Padding.fixed_10(self._build_buttons()),
]
super().__init__(ListBox(body))
def _build_model_inputs(self):
sl = [
Columns(
[
("weight", 0.2, Text("Email address:", align="right")),
("weight", 0.3,
Color.string_input(self.email,
focus_map="string_input focus"))
],
dividechars=4
),
]
return Pile(sl)
def _build_buttons(self):
cancel = cancel_btn(on_press=self.cancel)
done = done_btn(on_press=self.done)
buttons = [
Color.button(done, focus_map='button focus'),
Color.button(cancel, focus_map='button focus')
]
return Pile(buttons)
def cancel(self, button):
self.signal.prev_signal()
def done(self, button):
if len(self.email.value) < 1:
self.error.set_text("Please enter an email address.")
return
if not self.opts.dry_run:
self.progress.set_text("Contacting store...")
self.loop.draw_screen()
users_before = users()
result = run_command(["snap", "create-user", self.email.value])
self.progress.set_text("")
if result['status'] != 0:
self.error.set_text("Creating user failed:\n" + result['err'])
return
else:
users_after = users()
new_users = users_after - users_before
if len(new_users) != 1:
self.error.set_text("uhh")
return
new_user = pwd.getpwnam(new_users.pop())
# Use email for realname until
# https://bugs.launchpad.net/snappy/+bug/1607121 is resolved.
result = {
'realname': self.email.value, #new_user.pw_gecos.split(",")[0]
'username': new_user.pw_name,
'passwod': '',
'confirm_password': ''
}
# Work around https://bugs.launchpad.net/snappy/+bug/1606815
run_command(["chown", "{}:{}".format(new_user.pw_uid, new_user.pw_gid), "-R", new_user.pw_dir])
self.model.add_user(result)
else:
result = {
'realname': self.email.value,
'username': self.email.value,
'passwod': '',
'confirm_password': '',
}
self.model.add_user(result)
self.signal.emit_signal('menu:identity:login:main')
def users():
r = set()
for pw in pwd.getpwall():
r.add(pw.pw_name)
return r

10
debian/changelog vendored
View File

@ -1,7 +1,11 @@
subiquity (0.0.6~cy3) UNRELEASED; urgency=medium subiquity (0.0.7~1) UNRELEASED; urgency=medium
* Release 0.0.6 snapshot: port for yakkety: [ Michael Hudson-Doyle ]
- Build separate subiquitycore, subiquity, and console-conf packages. * Demo-ware.
* Install systemd units for console-conf.
[ Mathieu Trudel-Lapierre ]
* Release 0.0.7 snapshot
* debian/control: move Depends on curtin from subiquitycore to subiquity. * debian/control: move Depends on curtin from subiquitycore to subiquity.
-- Mathieu Trudel-Lapierre <cyphermox@ubuntu.com> Fri, 08 Jul 2016 16:44:55 -0400 -- Mathieu Trudel-Lapierre <cyphermox@ubuntu.com> Fri, 08 Jul 2016 16:44:55 -0400

View File

@ -7,7 +7,7 @@ ExecStop=systemctl start getty@%I
Before=getty.target Before=getty.target
IgnoreOnIsolate=yes IgnoreOnIsolate=yes
ConditionPathExists=/dev/tty0 ConditionPathExists=/dev/tty0
ConditionPathExists=!/var/lib/firstboot/firstboot-complete ConditionPathExists=!/writable/firstboot-complete
[Service] [Service]
Environment=PYTHONPATH=/usr/share/subiquity Environment=PYTHONPATH=/usr/share/subiquity

6
debian/console-conf.postinst vendored Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
#DEBHELPER#
systemctl enable console-conf@tty1.service
systemctl enable serial-console-conf@ttyS1.service

View File

@ -4,7 +4,7 @@ BindsTo=dev-%i.device
#After=getty@tty.service #After=getty@tty.service
After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service After=rc-local.service
ConditionPathExists=!/var/lib/firstboot/firstboot-complete ConditionPathExists=!/writable/firstboot-complete
[Service] [Service]
Environment=PYTHONPATH=/usr/share/subiquity Environment=PYTHONPATH=/usr/share/subiquity

View File

@ -18,14 +18,15 @@ import os
import sys import sys
from logging.handlers import TimedRotatingFileHandler from logging.handlers import TimedRotatingFileHandler
LOGDIR = ".subiquity" LOGDIR = "/writable/.subiquity"
LOGFILE = os.path.join(LOGDIR, "subiquity-debug.log") LOGFILE = os.path.join(LOGDIR, "subiquity-debug.log")
def setup_logger(name=__name__): def setup_logger(name=__name__, dir=LOGDIR):
LOGFILE = os.path.join(dir, "subiquity-debug.log")
try: try:
if not os.path.isdir(LOGDIR): if not os.path.isdir(dir):
os.makedirs(LOGDIR) os.makedirs(dir)
log = TimedRotatingFileHandler(LOGFILE, log = TimedRotatingFileHandler(LOGFILE,
when='D', when='D',
interval=1, interval=1,
@ -46,4 +47,4 @@ def setup_logger(name=__name__):
logger = logging.getLogger('') logger = logging.getLogger('')
logger.setLevel('DEBUG') logger.setLevel('DEBUG')
logger.addHandler(log) logger.addHandler(log)
return logger return LOGFILE

View File

@ -74,6 +74,20 @@ class RealnameEditor(StringEditor):
return super().keypress(size, key) return super().keypress(size, key)
class EmailEditor(StringEditor):
""" Email input prompt with input rules
"""
def keypress(self, size, key):
''' restrict what chars we allow for username '''
realname = r'[-a-zA-Z0-9_.@]'
if re.match(realname, key) is None:
return False
return super().keypress(size, key)
class UsernameEditor(StringEditor): class UsernameEditor(StringEditor):
""" Username input prompt with input rules """ Username input prompt with input rules
""" """

View File

@ -202,7 +202,7 @@ def sudo_user():
def mark_firstboot_complete(): def mark_firstboot_complete():
""" Touch our firstboot-complete eyecatcher """ """ Touch our firstboot-complete eyecatcher """
log.info('marking firstboot service complete') log.info('marking firstboot service complete')
firstboot = '/var/lib/firstboot/firstboot-complete' firstboot = '/writable/firstboot-complete'
if not os.path.exists(os.path.dirname(firstboot)): if not os.path.exists(os.path.dirname(firstboot)):
os.makedirs(os.path.dirname(firstboot)) os.makedirs(os.path.dirname(firstboot))
with open(firstboot, 'w') as fp: with open(firstboot, 'w') as fp: