Merge pull request #1140 from ogayot/FR-1652
Add initial integration of Ubuntu Advantage in Subiquity
This commit is contained in:
commit
73bc16b8fb
|
@ -45,6 +45,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: false
|
install_server: false
|
||||||
SnapList:
|
SnapList:
|
||||||
|
|
|
@ -20,6 +20,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: false
|
install_server: false
|
||||||
SnapList:
|
SnapList:
|
||||||
|
|
|
@ -20,6 +20,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: true
|
install_server: true
|
||||||
pwauth: false
|
pwauth: false
|
||||||
|
|
|
@ -64,6 +64,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: true
|
install_server: true
|
||||||
pwauth: false
|
pwauth: false
|
||||||
|
|
|
@ -45,6 +45,8 @@ Filesystem:
|
||||||
fstype: ext4
|
fstype: ext4
|
||||||
mount: /
|
mount: /
|
||||||
- action: done
|
- action: done
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
Identity:
|
Identity:
|
||||||
realname: Ubuntu
|
realname: Ubuntu
|
||||||
username: ubuntu
|
username: ubuntu
|
||||||
|
|
|
@ -25,6 +25,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: true
|
install_server: true
|
||||||
pwauth: false
|
pwauth: false
|
||||||
|
|
|
@ -96,6 +96,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: false
|
install_server: false
|
||||||
SnapList:
|
SnapList:
|
||||||
|
|
|
@ -55,6 +55,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: false
|
install_server: false
|
||||||
SnapList:
|
SnapList:
|
||||||
|
|
|
@ -24,6 +24,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: true
|
install_server: true
|
||||||
pwauth: false
|
pwauth: false
|
||||||
|
|
|
@ -28,6 +28,8 @@ Identity:
|
||||||
hostname: ubuntu-server
|
hostname: ubuntu-server
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SSH:
|
SSH:
|
||||||
install_server: false
|
install_server: false
|
||||||
SnapList:
|
SnapList:
|
||||||
|
|
|
@ -22,6 +22,8 @@ Identity:
|
||||||
# ubuntu
|
# ubuntu
|
||||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||||
ssh-import-id: gh:mwhudson
|
ssh-import-id: gh:mwhudson
|
||||||
|
UbuntuAdvantage:
|
||||||
|
token: "a1b2c3d4"
|
||||||
SnapList:
|
SnapList:
|
||||||
snaps:
|
snaps:
|
||||||
hello:
|
hello:
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
DISTRIB_ID=Ubuntu
|
||||||
|
DISTRIB_RELEASE=20.04
|
||||||
|
DISTRIB_CODENAME=focal
|
||||||
|
DISTRIB_DESCRIPTION="Ubuntu 20.04 LTS"
|
|
@ -0,0 +1,4 @@
|
||||||
|
DISTRIB_ID=Ubuntu
|
||||||
|
DISTRIB_RELEASE=21.10
|
||||||
|
DISTRIB_CODENAME=impish
|
||||||
|
DISTRIB_DESCRIPTION="Ubuntu 21.10"
|
|
@ -110,6 +110,7 @@ class SubiquityClient(TuiApplication):
|
||||||
"Refresh",
|
"Refresh",
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Identity",
|
"Identity",
|
||||||
|
"UbuntuAdvantage",
|
||||||
"SSH",
|
"SSH",
|
||||||
"SnapList",
|
"SnapList",
|
||||||
"Progress",
|
"Progress",
|
||||||
|
|
|
@ -26,6 +26,7 @@ from .serial import SerialController
|
||||||
from .snaplist import SnapListController
|
from .snaplist import SnapListController
|
||||||
from .source import SourceController
|
from .source import SourceController
|
||||||
from .ssh import SSHController
|
from .ssh import SSHController
|
||||||
|
from .ubuntu_advantage import UbuntuAdvantageController
|
||||||
from .welcome import WelcomeController
|
from .welcome import WelcomeController
|
||||||
from .zdev import ZdevController
|
from .zdev import ZdevController
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ __all__ = [
|
||||||
'SnapListController',
|
'SnapListController',
|
||||||
'SourceController',
|
'SourceController',
|
||||||
'SSHController',
|
'SSHController',
|
||||||
|
'UbuntuAdvantageController',
|
||||||
'WelcomeController',
|
'WelcomeController',
|
||||||
'ZdevController',
|
'ZdevController',
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Copyright 2021 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/>.
|
||||||
|
""" Module that defines the client-side controller class for Ubuntu Advantage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from subiquity.client.controller import SubiquityTuiController
|
||||||
|
from subiquity.common.types import UbuntuAdvantageInfo
|
||||||
|
from subiquity.ui.views.ubuntu_advantage import UbuntuAdvantageView
|
||||||
|
|
||||||
|
from subiquitycore.lsb_release import lsb_release
|
||||||
|
from subiquitycore.tuicontroller import Skip
|
||||||
|
|
||||||
|
log = logging.getLogger("subiquity.client.controllers.ubuntu_advantage")
|
||||||
|
|
||||||
|
|
||||||
|
class UbuntuAdvantageController(SubiquityTuiController):
|
||||||
|
""" Client-side controller for Ubuntu Advantage configuration. """
|
||||||
|
|
||||||
|
endpoint_name = "ubuntu_advantage"
|
||||||
|
|
||||||
|
async def make_ui(self) -> UbuntuAdvantageView:
|
||||||
|
""" Generate the UI, based on the data provided by the model. """
|
||||||
|
|
||||||
|
# TODO remove these two lines to enable this screen
|
||||||
|
await self.endpoint.skip.POST()
|
||||||
|
raise Skip("Hiding the screen until we can validate the token.")
|
||||||
|
|
||||||
|
path_lsb_release: Optional[str] = None
|
||||||
|
if self.app.opts.dry_run:
|
||||||
|
# In dry-run mode, always pretend to be on LTS
|
||||||
|
path_lsb_release = "examples/lsb-release-focal"
|
||||||
|
if "LTS" not in lsb_release(path_lsb_release)["description"]:
|
||||||
|
await self.endpoint.skip.POST()
|
||||||
|
raise Skip("Not running LTS version")
|
||||||
|
|
||||||
|
ubuntu_advantage_info = await self.endpoint.GET()
|
||||||
|
return UbuntuAdvantageView(self, ubuntu_advantage_info.token)
|
||||||
|
|
||||||
|
def run_answers(self) -> None:
|
||||||
|
if "token" in self.answers:
|
||||||
|
self.done(self.answers["token"])
|
||||||
|
|
||||||
|
def cancel(self) -> None:
|
||||||
|
self.app.prev_screen()
|
||||||
|
|
||||||
|
def done(self, token: str) -> None:
|
||||||
|
log.debug("UbuntuAdvantageController.done token=%s", token)
|
||||||
|
self.app.next_screen(
|
||||||
|
self.endpoint.POST(UbuntuAdvantageInfo(token=token))
|
||||||
|
)
|
|
@ -48,6 +48,7 @@ from subiquity.common.types import (
|
||||||
StorageResponse,
|
StorageResponse,
|
||||||
StorageResponseV2,
|
StorageResponseV2,
|
||||||
TimeZoneInfo,
|
TimeZoneInfo,
|
||||||
|
UbuntuAdvantageInfo,
|
||||||
WLANSupportInstallState,
|
WLANSupportInstallState,
|
||||||
ZdevInfo,
|
ZdevInfo,
|
||||||
WSLConfigurationBase,
|
WSLConfigurationBase,
|
||||||
|
@ -312,6 +313,13 @@ class API:
|
||||||
def GET() -> List[str]: ...
|
def GET() -> List[str]: ...
|
||||||
def POST(data: Payload[List[str]]): ...
|
def POST(data: Payload[List[str]]): ...
|
||||||
|
|
||||||
|
class ubuntu_advantage:
|
||||||
|
def GET() -> UbuntuAdvantageInfo: ...
|
||||||
|
def POST(data: Payload[UbuntuAdvantageInfo]) -> None: ...
|
||||||
|
|
||||||
|
class skip:
|
||||||
|
def POST() -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class LinkAction(enum.Enum):
|
class LinkAction(enum.Enum):
|
||||||
NEW = enum.auto()
|
NEW = enum.auto()
|
||||||
|
|
|
@ -389,6 +389,11 @@ class TimeZoneInfo:
|
||||||
from_geoip: bool
|
from_geoip: bool
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(auto_attribs=True)
|
||||||
|
class UbuntuAdvantageInfo:
|
||||||
|
token: str
|
||||||
|
|
||||||
|
|
||||||
class ShutdownMode(enum.Enum):
|
class ShutdownMode(enum.Enum):
|
||||||
REBOOT = enum.auto()
|
REBOOT = enum.auto()
|
||||||
POWEROFF = enum.auto()
|
POWEROFF = enum.auto()
|
||||||
|
|
|
@ -41,6 +41,7 @@ from .snaplist import SnapListModel
|
||||||
from .source import SourceModel
|
from .source import SourceModel
|
||||||
from .ssh import SSHModel
|
from .ssh import SSHModel
|
||||||
from .timezone import TimeZoneModel
|
from .timezone import TimeZoneModel
|
||||||
|
from .ubuntu_advantage import UbuntuAdvantageModel
|
||||||
from .updates import UpdatesModel
|
from .updates import UpdatesModel
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,6 +144,7 @@ class SubiquityModel:
|
||||||
self.ssh = SSHModel()
|
self.ssh = SSHModel()
|
||||||
self.source = SourceModel()
|
self.source = SourceModel()
|
||||||
self.timezone = TimeZoneModel()
|
self.timezone = TimeZoneModel()
|
||||||
|
self.ubuntu_advantage = UbuntuAdvantageModel()
|
||||||
self.updates = UpdatesModel()
|
self.updates = UpdatesModel()
|
||||||
self.userdata = {}
|
self.userdata = {}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Copyright 2021 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 unittest
|
||||||
|
|
||||||
|
from subiquity.models.ubuntu_advantage import UbuntuAdvantageModel
|
||||||
|
|
||||||
|
|
||||||
|
class TestUbuntuAdvantageModel(unittest.TestCase):
|
||||||
|
def test_make_cloudconfig_(self):
|
||||||
|
model = UbuntuAdvantageModel()
|
||||||
|
|
||||||
|
# Test with a token
|
||||||
|
model.token = "0a1b2c3d4e5f6"
|
||||||
|
expected = {
|
||||||
|
"ubuntu_advantage": {
|
||||||
|
"token": "0a1b2c3d4e5f6",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.assertEqual(model.make_cloudconfig(), expected)
|
||||||
|
|
||||||
|
# Test without token
|
||||||
|
model.token = ""
|
||||||
|
self.assertEqual(model.make_cloudconfig(), {})
|
||||||
|
model.token = None
|
||||||
|
self.assertEqual(model.make_cloudconfig(), {})
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Copyright 2021 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/>.
|
||||||
|
""" Module that defines the model for Ubuntu Advantage configuration. """
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
log = logging.getLogger("subiquitycore.models.ubuntu_advantage")
|
||||||
|
|
||||||
|
|
||||||
|
class UbuntuAdvantageModel:
|
||||||
|
"""
|
||||||
|
Model that represents the Ubuntu Advantage configuration.
|
||||||
|
Currently, we rely only on cloud-init so we have no means to validate that
|
||||||
|
the provided token is correct ; nor to retrieve information about the
|
||||||
|
subscription.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
""" Initialize the model. """
|
||||||
|
self.token: str = ""
|
||||||
|
|
||||||
|
def make_cloudconfig(self) -> dict:
|
||||||
|
"""
|
||||||
|
Return a dictionary that will be included in cloud-init config.
|
||||||
|
Having the token set to the empty-string disables the configuration.
|
||||||
|
"""
|
||||||
|
if not self.token:
|
||||||
|
return {}
|
||||||
|
# Both "ubuntu_advantage" and "ubuntu-advantage" keys are accepted, but
|
||||||
|
# "ubuntu-advantage" is deprecated despite not being mentioned in the
|
||||||
|
# documentation.
|
||||||
|
return {
|
||||||
|
"ubuntu_advantage": {
|
||||||
|
"token": self.token,
|
||||||
|
},
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ from .snaplist import SnapListController
|
||||||
from .source import SourceController
|
from .source import SourceController
|
||||||
from .ssh import SSHController
|
from .ssh import SSHController
|
||||||
from .timezone import TimeZoneController
|
from .timezone import TimeZoneController
|
||||||
|
from .ubuntu_advantage import UbuntuAdvantageController
|
||||||
from .updates import UpdatesController
|
from .updates import UpdatesController
|
||||||
from .userdata import UserdataController
|
from .userdata import UserdataController
|
||||||
from .zdev import ZdevController
|
from .zdev import ZdevController
|
||||||
|
@ -58,6 +59,7 @@ __all__ = [
|
||||||
'SourceController',
|
'SourceController',
|
||||||
'SSHController',
|
'SSHController',
|
||||||
'TimeZoneController',
|
'TimeZoneController',
|
||||||
|
'UbuntuAdvantageController',
|
||||||
'UpdatesController',
|
'UpdatesController',
|
||||||
'UserdataController',
|
'UserdataController',
|
||||||
'ZdevController',
|
'ZdevController',
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright 2021 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 unittest
|
||||||
|
|
||||||
|
from subiquity.server.controllers.ubuntu_advantage import (
|
||||||
|
UbuntuAdvantageController,
|
||||||
|
)
|
||||||
|
from subiquitycore.tests.mocks import make_app
|
||||||
|
|
||||||
|
|
||||||
|
class TestUbuntuAdvantageController(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.controller = UbuntuAdvantageController(make_app())
|
||||||
|
|
||||||
|
def test_serialize(self):
|
||||||
|
self.controller.model.token = "1a2b3C"
|
||||||
|
self.assertEqual(self.controller.serialize(), "1a2b3C")
|
||||||
|
|
||||||
|
def test_deserialize(self):
|
||||||
|
self.controller.deserialize("1A2B3C4D")
|
||||||
|
self.assertEqual(self.controller.model.token, "1A2B3C4D")
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Copyright 2021 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/>.
|
||||||
|
""" Module defining the server-side controller class for Ubuntu Advantage. """
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from subiquity.common.apidef import API
|
||||||
|
from subiquity.common.types import UbuntuAdvantageInfo
|
||||||
|
from subiquity.server.controller import SubiquityController
|
||||||
|
|
||||||
|
log = logging.getLogger("subiquity.server.controllers.ubuntu_advantage")
|
||||||
|
|
||||||
|
|
||||||
|
class UbuntuAdvantageController(SubiquityController):
|
||||||
|
""" Represent the server-side Ubuntu Advantage controller. """
|
||||||
|
|
||||||
|
endpoint = API.ubuntu_advantage
|
||||||
|
|
||||||
|
model_name = "ubuntu_advantage"
|
||||||
|
|
||||||
|
def serialize(self) -> str:
|
||||||
|
""" Save the current state of the model so it can be loaded later.
|
||||||
|
Currently this function is called automatically by .configured().
|
||||||
|
"""
|
||||||
|
return self.model.token
|
||||||
|
|
||||||
|
def deserialize(self, token: str) -> None:
|
||||||
|
""" Loads the last-known state of the model. """
|
||||||
|
self.model.token = token
|
||||||
|
|
||||||
|
async def GET(self) -> UbuntuAdvantageInfo:
|
||||||
|
""" Handle a GET request coming from the client-side controller. """
|
||||||
|
return UbuntuAdvantageInfo(token=self.model.token)
|
||||||
|
|
||||||
|
async def POST(self, data: UbuntuAdvantageInfo) -> None:
|
||||||
|
""" Handle a POST request coming from the client-side controller and
|
||||||
|
then call .configured().
|
||||||
|
"""
|
||||||
|
log.debug("Received POST: %s", data)
|
||||||
|
self.model.token = data.token
|
||||||
|
await self.configured()
|
||||||
|
|
||||||
|
async def skip_POST(self) -> None:
|
||||||
|
""" When running on a non-LTS release, we want to call this so we can
|
||||||
|
skip the screen on the client side. """
|
||||||
|
await self.configured()
|
|
@ -204,6 +204,7 @@ POSTINSTALL_MODEL_NAMES = ModelNames({
|
||||||
"packages",
|
"packages",
|
||||||
"snaplist",
|
"snaplist",
|
||||||
"ssh",
|
"ssh",
|
||||||
|
"ubuntu_advantage",
|
||||||
"userdata",
|
"userdata",
|
||||||
},
|
},
|
||||||
desktop={"timezone"})
|
desktop={"timezone"})
|
||||||
|
@ -242,6 +243,7 @@ class SubiquityServer(Application):
|
||||||
"Zdev",
|
"Zdev",
|
||||||
"Source",
|
"Source",
|
||||||
"Network",
|
"Network",
|
||||||
|
"UbuntuAdvantage",
|
||||||
"Proxy",
|
"Proxy",
|
||||||
"Mirror",
|
"Mirror",
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
|
|
|
@ -215,6 +215,10 @@ class TestFlow(TestAPI):
|
||||||
}
|
}
|
||||||
await inst.post('/ssh', ssh)
|
await inst.post('/ssh', ssh)
|
||||||
await inst.post('/snaplist', [])
|
await inst.post('/snaplist', [])
|
||||||
|
ua_params = {
|
||||||
|
"token": "a1b2c3d4e6f7g8h9I0K1",
|
||||||
|
}
|
||||||
|
await inst.post('/ubuntu_advantage', ua_params)
|
||||||
for state in 'RUNNING', 'POST_WAIT', 'POST_RUNNING', 'UU_RUNNING':
|
for state in 'RUNNING', 'POST_WAIT', 'POST_RUNNING', 'UU_RUNNING':
|
||||||
await inst.get('/meta/status', cur=state)
|
await inst.get('/meta/status', cur=state)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
# Copyright 2021 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/>.
|
||||||
|
""" Module that defines the view class for Ubuntu Advantage configuration. """
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from urwid import connect_signal
|
||||||
|
|
||||||
|
from subiquitycore.view import BaseView
|
||||||
|
from subiquitycore.ui.form import (
|
||||||
|
Form,
|
||||||
|
simple_field,
|
||||||
|
WantsToKnowFormField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from subiquitycore.ui.interactive import StringEditor
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger('subiquity.ui.views.ubuntu_advantage')
|
||||||
|
|
||||||
|
ua_help = _("If you want to enroll this system using your Ubuntu Advantage "
|
||||||
|
"subscription, enter your Ubuntu Advantage token here. "
|
||||||
|
"Otherwise, leave this blank.")
|
||||||
|
|
||||||
|
|
||||||
|
class UATokenEditor(StringEditor, WantsToKnowFormField):
|
||||||
|
""" Represent a text-box editor for the Ubuntu Advantage Token. """
|
||||||
|
def __init__(self):
|
||||||
|
""" Initialize the text-field editor for UA token. """
|
||||||
|
self.valid_char_pat = r"[a-zA-Z0-9]"
|
||||||
|
self.error_invalid_char = _("The only characters permitted in this "
|
||||||
|
"field are alphanumeric characters.")
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def valid_char(self, ch: str) -> bool:
|
||||||
|
""" Tells whether the character passed is within the range of allowed
|
||||||
|
characters
|
||||||
|
"""
|
||||||
|
if len(ch) == 1 and not re.match(self.valid_char_pat, ch):
|
||||||
|
self.bff.in_error = True
|
||||||
|
self.bff.show_extra(("info_error", self.error_invalid_char))
|
||||||
|
return False
|
||||||
|
return super().valid_char(ch)
|
||||||
|
|
||||||
|
|
||||||
|
class UbuntuAdvantageForm(Form):
|
||||||
|
"""
|
||||||
|
Represents a form requesting Ubuntu Advantage information
|
||||||
|
"""
|
||||||
|
cancel_label = _("Back")
|
||||||
|
|
||||||
|
UATokenField = simple_field(UATokenEditor)
|
||||||
|
|
||||||
|
token = UATokenField(_("Ubuntu Advantage token:"), help=ua_help)
|
||||||
|
|
||||||
|
|
||||||
|
class UbuntuAdvantageView(BaseView):
|
||||||
|
""" Represent the view of the Ubuntu Advantage configuration. """
|
||||||
|
|
||||||
|
title = _("Enable Ubuntu Advantage")
|
||||||
|
excerpt = _("Enter your Ubuntu Advantage token if you want to enroll "
|
||||||
|
"this system.")
|
||||||
|
|
||||||
|
def __init__(self, controller, token: str):
|
||||||
|
""" Initialize the view with the default value for the token. """
|
||||||
|
self.controller = controller
|
||||||
|
|
||||||
|
self.form = UbuntuAdvantageForm(initial={"token": token})
|
||||||
|
|
||||||
|
def on_cancel(_: UbuntuAdvantageForm):
|
||||||
|
self.cancel()
|
||||||
|
|
||||||
|
connect_signal(self.form, 'submit', self.done)
|
||||||
|
connect_signal(self.form, 'cancel', on_cancel)
|
||||||
|
|
||||||
|
super().__init__(self.form.as_screen(excerpt=_(self.excerpt)))
|
||||||
|
|
||||||
|
def done(self, form: UbuntuAdvantageForm) -> None:
|
||||||
|
""" Called when the user presses the Done button. """
|
||||||
|
log.debug("User input: %r", form.as_data())
|
||||||
|
|
||||||
|
self.controller.done(form.token.value)
|
||||||
|
|
||||||
|
def cancel(self) -> None:
|
||||||
|
""" Called when the user presses the Back button. """
|
||||||
|
self.controller.cancel()
|
Loading…
Reference in New Issue