Merge pull request #749 from mwhudson/better-nested-contexts

make it easier to nest contexts appropriately
This commit is contained in:
Michael Hudson-Doyle 2020-05-04 19:36:26 +12:00 committed by GitHub
commit 9ea34e8b0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 274 additions and 252 deletions

View File

@ -43,9 +43,9 @@ python3 scripts/check-yaml-fields.py .subiquity/subiquity-curtin-install.conf \
debconf_selections.subiquity='"eek"'
python3 scripts/check-yaml-fields.py .subiquity/var/lib/cloud/seed/nocloud-net/user-data \
locale='"en_UK.UTF-8"'
grep -q 'finish: subiquity/InstallProgress/postinstall/install_package1: SUCCESS: installing package1' \
grep -q 'finish: subiquity/InstallProgress/install/postinstall/install_package1: SUCCESS: installing package1' \
.subiquity/subiquity-debug.log
grep -q 'finish: subiquity/InstallProgress/postinstall/install_package2: SUCCESS: installing package2' \
grep -q 'finish: subiquity/InstallProgress/install/postinstall/install_package2: SUCCESS: installing package2' \
.subiquity/subiquity-debug.log
grep -q 'switching subiquity to edge' .subiquity/subiquity-debug.log

View File

@ -17,6 +17,7 @@ import logging
import jsonschema
from subiquitycore.context import with_context
from subiquitycore.controller import (
BaseController,
RepeatedController,
@ -57,7 +58,8 @@ class SubiquityController(BaseController):
"""
pass
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
"""Apply autoinstall configuration.
This is only called for a non-interactive controller. It should
@ -110,7 +112,7 @@ class RepeatedController(RepeatedController):
self.autoinstall_applied = False
async def apply_autoinstall_config(self):
await self.orig.apply_autoinstall_config(self.index)
await self.orig.apply_autoinstall_config(index=self.index)
def configured(self):
self.orig.configured()

View File

@ -13,6 +13,7 @@
# 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.context import with_context
from subiquitycore.utils import arun_command
from subiquity.controller import NoUIController
@ -34,7 +35,8 @@ class CmdListController(NoUIController):
def load_autoinstall_data(self, data):
self.cmds = data
async def run(self):
@with_context()
async def run(self, context):
for i, cmd in enumerate(self.cmds):
with self.context.child("command_{}".format(i), cmd):
if isinstance(cmd, str):
@ -54,5 +56,6 @@ class LateController(CmdListController):
autoinstall_key = 'late-commands'
async def apply_autoinstall_config(self):
await self.run()
@with_context()
async def apply_autoinstall_config(self, context):
await self.run(context)

View File

@ -26,6 +26,7 @@ from subiquitycore.async_helpers import (
schedule_task,
SingleInstanceTask,
)
from subiquitycore.context import with_context
from subiquitycore.lsb_release import lsb_release
from subiquitycore.utils import (
run_command,
@ -102,7 +103,8 @@ class FilesystemController(SubiquityController):
log.debug("self.ai_data = %s", data)
self.ai_data = data
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
self.stop_listening_udev()
await self._start_task
await self._probe_task.wait()

View File

@ -17,6 +17,8 @@ import logging
import attr
from subiquitycore.context import with_context
from subiquity.controller import SubiquityController
from subiquity.ui.views import IdentityView
@ -42,7 +44,8 @@ class IdentityController(SubiquityController):
if data is not None:
self.model.add_user(data)
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
if not self.model.user:
if 'user-data' not in self.app.autoinstall_config:
raise Exception("no identity data provided")

View File

@ -14,7 +14,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import contextlib
import datetime
import logging
import os
@ -38,7 +37,7 @@ from subiquitycore.async_helpers import (
run_in_thread,
schedule_task,
)
from subiquitycore.context import Status
from subiquitycore.context import Status, with_context
from subiquitycore.utils import (
arun_command,
astart_command,
@ -79,25 +78,13 @@ class TracebackExtractor:
self.traceback.append(line)
def install_step(label, level=None, childlevel=None):
def decorate(meth):
name = meth.__name__
async def decorated(self, context, *args):
manager = self.install_context(
context, name, label, level, childlevel)
with manager as subcontext:
await meth(self, subcontext, *args)
return decorated
return decorate
class InstallProgressController(SubiquityController):
def __init__(self, app):
super().__init__(app)
self.model = app.base_model
self.progress_view = ProgressView(self)
app.add_event_listener(self)
self.install_state = InstallState.NOT_STARTED
self.reboot_clicked = asyncio.Event()
@ -116,12 +103,38 @@ class InstallProgressController(SubiquityController):
return self.app.interactive()
def start(self):
self.install_task = schedule_task(self.install(self.context))
self.install_task = schedule_task(self.install())
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
await self.install_task
self.app.reboot_on_exit = True
def _push_to_progress(self, context):
if not self.app.interactive():
return False
if context.get('hidden', False):
return False
controller = context.get('controller')
if controller is None or controller.interactive():
return False
return True
def report_start_event(self, context, description):
if self._push_to_progress(context):
msg = context.full_name()
if description:
msg += ': ' + description
self.progress_view.event_start(context, msg)
if context.get('is-install-context'):
self.progress_view.event_start(context, context.description)
def report_finish_event(self, context, description, status):
if self._push_to_progress(context):
self.progress_view.event_finish(context)
if context.get('is-install-context'):
self.progress_view.event_finish(context)
def tpath(self, *path):
return os.path.join(self.model.target, *path)
@ -152,17 +165,6 @@ class InstallProgressController(SubiquityController):
elif event['SYSLOG_IDENTIFIER'] == self._log_syslog_identifier:
self.curtin_log(event)
@contextlib.contextmanager
def install_context(self, context, name, description,
level=None, childlevel=None):
subcontext = context.child(name, description, level, childlevel)
self.progress_view.event_start(subcontext, description)
try:
with subcontext:
yield subcontext
finally:
self.progress_view.event_finish(subcontext)
def curtin_event(self, event):
e = {
"EVENT_TYPE": "???",
@ -190,13 +192,11 @@ class InstallProgressController(SubiquityController):
break
if curtin_ctx:
curtin_ctx.enter()
self.progress_view.event_start(curtin_ctx, e["MESSAGE"])
if event_type == 'finish':
status = getattr(Status, e["RESULT"], Status.WARN)
curtin_ctx = self.curtin_event_contexts.pop(e["NAME"], None)
if curtin_ctx is not None:
curtin_ctx.exit(status)
self.progress_view.event_finish(curtin_ctx)
def curtin_log(self, event):
log_line = event['MESSAGE']
@ -240,7 +240,7 @@ class InstallProgressController(SubiquityController):
return curtin_cmd
@install_step("umounting /target dir")
@with_context(description="umounting /target dir")
async def unmount_target(self, context, target):
cmd = [
sys.executable, '-m', 'curtin', 'unmount',
@ -252,7 +252,8 @@ class InstallProgressController(SubiquityController):
if not self.opts.dry_run:
shutil.rmtree(target)
@install_step("installing system", level="INFO", childlevel="DEBUG")
@with_context(
description="installing system", level="INFO", childlevel="DEBUG")
async def curtin_install(self, context):
log.debug('curtin_install')
self.install_state = InstallState.RUNNING
@ -286,7 +287,9 @@ class InstallProgressController(SubiquityController):
def cancel(self):
pass
@with_context()
async def install(self, context):
context.set('is-install-context', True)
try:
await asyncio.wait(
{e.wait() for e in self.model.install_events})
@ -331,8 +334,9 @@ class InstallProgressController(SubiquityController):
log.debug("waited %s seconds for events to drain", waited)
self.curtin_event_contexts.pop('', None)
@install_step(
"final system configuration", level="INFO", childlevel="DEBUG")
@with_context(
description="final system configuration", level="INFO",
childlevel="DEBUG")
async def postinstall(self, context):
autoinstall_path = os.path.join(
self.app.root, 'var/log/installer/autoinstall-user-data')
@ -345,15 +349,14 @@ class InstallProgressController(SubiquityController):
packages = ['openssh-server']
packages.extend(self.app.base_model.packages)
for package in packages:
subcontext = self.install_context(
context,
subcontext = context.child(
"install_{}".format(package),
"installing {}".format(package))
with subcontext:
await self.install_package(package)
await self.restore_apt_config(context)
@install_step("configuring cloud-init")
@with_context(description="configuring cloud-init")
async def configure_cloud_init(self, context):
await run_in_thread(self.model.configure_cloud_init)
@ -368,7 +371,7 @@ class InstallProgressController(SubiquityController):
]
await arun_command(self.logged_command(cmd), check=True)
@install_step("restoring apt configuration")
@with_context(description="restoring apt configuration")
async def restore_apt_config(self, context):
if self.opts.dry_run:
cmds = [["sleep", str(1/self.app.scale_factor)]]
@ -386,7 +389,7 @@ class InstallProgressController(SubiquityController):
for cmd in cmds:
await arun_command(self.logged_command(cmd), check=True)
@install_step("downloading and installing security updates")
@with_context(description="downloading and installing security updates")
async def run_unattended_upgrades(self, context):
target_tmp = os.path.join(self.model.target, "tmp")
os.makedirs(target_tmp, exist_ok=True)
@ -414,8 +417,7 @@ class InstallProgressController(SubiquityController):
async def stop_unattended_upgrades(self):
self.progress_view.event_finish(self.unattended_upgrades_ctx)
with self.install_context(
self.unattended_upgrades_ctx.parent,
with self.unattended_upgrades_ctx.parent.child(
"stop_unattended_upgrades",
"cancelling update"):
if self.opts.dry_run:

View File

@ -18,6 +18,7 @@ import logging
import attr
from subiquitycore.async_helpers import schedule_task
from subiquitycore.context import with_context
from subiquity.controller import SubiquityController
from subiquity.models.keyboard import KeyboardSetting
@ -47,7 +48,8 @@ class KeyboardController(SubiquityController):
if data is not None:
self.model.setting = KeyboardSetting(**data)
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
await self.model.set_keyboard(self.model.setting)
def language_selected(self, code):

View File

@ -25,6 +25,7 @@ from subiquitycore.async_helpers import (
run_in_thread,
SingleInstanceTask,
)
from subiquitycore.context import with_context
from subiquity.controller import SubiquityController
from subiquity.ui.views.mirror import MirrorView
@ -73,7 +74,8 @@ class MirrorController(SubiquityController):
merge_config(self.model.config, data)
self.geoip_enabled = geoip and self.model.is_default()
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
if not self.geoip_enabled:
return
try:

View File

@ -17,6 +17,7 @@ import asyncio
import logging
from subiquitycore.async_helpers import schedule_task
from subiquitycore.context import with_context
from subiquitycore.controllers.network import NetworkController
from subiquity.controller import SubiquityController
@ -110,11 +111,13 @@ class NetworkController(NetworkController, SubiquityController):
async def delay(self):
await asyncio.sleep(10)
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
if self.ai_data is None:
await self.initial_delay
with context.child("initial_delay"):
await self.initial_delay
self.update_initial_configs()
self.apply_config()
self.apply_config(context)
await self.apply_config_task.wait()
self.model.has_network = bool(
self.network_event_receiver.default_routes)
@ -129,9 +132,9 @@ class NetworkController(NetworkController, SubiquityController):
return {'network': r}
return super().render_config()
async def _apply_config(self, silent):
async def _apply_config(self, context=None, *, silent):
try:
await super()._apply_config(silent)
await super()._apply_config(context, silent=silent)
except asyncio.CancelledError:
# asyncio.CancelledError is a subclass of Exception in
# Python 3.6 (sadface)

View File

@ -16,6 +16,8 @@
import logging
import os
from subiquitycore.context import with_context
from subiquity.controller import SubiquityController
from subiquity.ui.views.proxy import ProxyView
@ -40,7 +42,8 @@ class ProxyController(SubiquityController):
self.model.proxy
self.signal.emit_signal('network-proxy-set')
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
# XXX want to wait until signal sent by .start() has been seen
# by everything; don't have a way to do that today.
pass

View File

@ -19,6 +19,7 @@ import platform
import subprocess
from subiquitycore.async_helpers import schedule_task
from subiquitycore.context import with_context
from subiquitycore.utils import arun_command, run_command
from subiquity.controller import SubiquityController
@ -63,7 +64,8 @@ class RebootController(SubiquityController):
run_command(["chreipl", "/target/boot"])
run_command(["/sbin/reboot"])
async def apply_autoinstall_config(self):
@with_context()
async def apply_autoinstall_config(self, context):
await self.copy_logs_to_target()
self.reboot()

View File

@ -24,6 +24,7 @@ from subiquitycore.async_helpers import (
schedule_task,
SingleInstanceTask,
)
from subiquitycore.context import with_context
from subiquitycore.controller import (
Skip,
)
@ -86,7 +87,8 @@ class RefreshController(SubiquityController):
self.check_for_update, propagate_errors=False)
self.check_task.start_sync()
async def apply_autoinstall_config(self, index=1):
@with_context()
async def apply_autoinstall_config(self, context, index=1):
if not self.active:
return
try:
@ -95,7 +97,7 @@ class RefreshController(SubiquityController):
return
if self.check_state != CheckState.AVAILABLE:
return
change_id = await self.start_update()
change_id = await self.start_update(context)
while True:
try:
change = await self.get_progress(change_id)
@ -120,33 +122,33 @@ class RefreshController(SubiquityController):
return CheckState.UNAVAILABLE
return task.result()
async def configure_snapd(self):
with self.context.child("configure_snapd") as context:
with context.child("get_details") as subcontext:
try:
r = await self.app.snapd.get(
'v2/snaps/{snap_name}'.format(
snap_name=self.snap_name))
except requests.exceptions.RequestException:
log.exception("getting snap details")
return
self.current_snap_version = r['result']['version']
for k in 'channel', 'revision', 'version':
self.app.note_data_for_apport(
"Snap" + k.title(), r['result'][k])
subcontext.description = "current version of snap is: %r" % (
self.current_snap_version)
channel = self.get_refresh_channel()
desc = "switching {} to {}".format(self.snap_name, channel)
with context.child("switching", desc) as subcontext:
try:
await self.app.snapd.post_and_wait(
'v2/snaps/{}'.format(self.snap_name),
{'action': 'switch', 'channel': channel})
except requests.exceptions.RequestException:
log.exception("switching channels")
return
subcontext.description = "switched to " + channel
@with_context()
async def configure_snapd(self, context):
with context.child("get_details") as subcontext:
try:
r = await self.app.snapd.get(
'v2/snaps/{snap_name}'.format(
snap_name=self.snap_name))
except requests.exceptions.RequestException:
log.exception("getting snap details")
return
self.current_snap_version = r['result']['version']
for k in 'channel', 'revision', 'version':
self.app.note_data_for_apport(
"Snap" + k.title(), r['result'][k])
subcontext.description = "current version of snap is: %r" % (
self.current_snap_version)
channel = self.get_refresh_channel()
desc = "switching {} to {}".format(self.snap_name, channel)
with context.child("switching", desc) as subcontext:
try:
await self.app.snapd.post_and_wait(
'v2/snaps/{}'.format(self.snap_name),
{'action': 'switch', 'channel': channel})
except requests.exceptions.RequestException:
log.exception("switching channels")
return
subcontext.description = "switched to " + channel
def get_refresh_channel(self):
"""Return the channel we should refresh subiquity to."""
@ -183,35 +185,33 @@ class RefreshController(SubiquityController):
if self.check_state == CheckState.UNKNOWN:
self.check_task.start_sync()
async def check_for_update(self):
@with_context()
async def check_for_update(self, context):
await asyncio.shield(self.configure_task)
with self.context.child("check_for_update") as context:
if self.app.updated:
context.description = (
"not offered update when already updated")
return CheckState.UNAVAILABLE
result = await self.app.snapd.get('v2/find', select='refresh')
log.debug("check_for_update received %s", result)
for snap in result["result"]:
if snap["name"] == self.snap_name:
self.new_snap_version = snap["version"]
context.description = (
"new version of snap available: %r"
% self.new_snap_version)
return CheckState.AVAILABLE
else:
context.description = (
"no new version of snap available")
if self.app.updated:
context.description = "not offered update when already updated"
return CheckState.UNAVAILABLE
result = await self.app.snapd.get('v2/find', select='refresh')
log.debug("check_for_update received %s", result)
for snap in result["result"]:
if snap["name"] == self.snap_name:
self.new_snap_version = snap["version"]
context.description = (
"new version of snap available: %r"
% self.new_snap_version)
return CheckState.AVAILABLE
else:
context.description = "no new version of snap available"
return CheckState.UNAVAILABLE
async def start_update(self):
@with_context()
async def start_update(self, context):
open(self.app.state_path('updating'), 'w').close()
with self.context.child("starting_update") as context:
change = await self.app.snapd.post(
'v2/snaps/{}'.format(self.snap_name),
{'action': 'refresh'})
context.description = "change id: {}".format(change)
return change
change = await self.app.snapd.post(
'v2/snaps/{}'.format(self.snap_name),
{'action': 'refresh'})
context.description = "change id: {}".format(change)
return change
async def get_progress(self, change):
result = await self.app.snapd.get('v2/changes/{}'.format(change))

View File

@ -64,6 +64,7 @@ class ReportingController(NoUIController):
def __init__(self, app):
self.config = copy.deepcopy(INITIAL_CONFIG)
super().__init__(app)
app.add_event_listener(self)
def load_autoinstall_data(self, data):
if self.app.interactive():
@ -75,9 +76,11 @@ class ReportingController(NoUIController):
def start(self):
update_configuration(self.config)
def report_start_event(self, name, description, level):
report_start_event(name, description, level=level)
def report_start_event(self, context, description):
report_start_event(
context.full_name(), description, level=context.level)
def report_finish_event(self, name, description, result, level):
def report_finish_event(self, context, description, result):
result = getattr(status, result.name, status.WARN)
report_finish_event(name, description, result, level=level)
report_finish_event(
context.full_name(), description, result, level=context.level)

View File

@ -128,6 +128,7 @@ class Subiquity(Application):
self.journal_fd, self.journal_watcher = journald_listener(
["subiquity"], self.subiquity_event, seek=True)
super().__init__(opts)
self.event_listeners = []
self.install_lock_file = Lockfile(self.state_path("installing"))
self.global_overlays = []
self.block_log_dir = block_log_dir
@ -288,41 +289,16 @@ class Subiquity(Application):
traceback.print_exc()
signal.pause()
def _push_to_progress(self, context):
if not self.interactive():
return False
InstallProgress = getattr(self.controllers, "InstallProgress", None)
if InstallProgress is None:
return False
if context.get('hidden', False):
return False
controller = context.get('controller')
if controller is None or controller.interactive():
return False
return True
def add_event_listener(self, listener):
self.event_listeners.append(listener)
def report_start_event(self, context, description):
# report_start_event gets called when the Reporting controller
# is being loaded...
Reporting = getattr(self.controllers, "Reporting", None)
if Reporting is not None:
Reporting.report_start_event(
context.full_name(), description, context.level)
if self._push_to_progress(context):
msg = context.full_name()
if description:
msg += ': ' + description
self.controllers.InstallProgress.progress_view.event_start(
context, msg)
for listener in self.event_listeners:
listener.report_start_event(context, description)
def report_finish_event(self, context, description, status):
Reporting = getattr(self.controllers, "Reporting", None)
if Reporting is not None:
Reporting.report_finish_event(
context.full_name(), description, status, context.level)
if self._push_to_progress(context):
self.controllers.InstallProgress.progress_view.event_finish(
context)
for listener in self.event_listeners:
listener.report_finish_event(context, description, status)
def confirm_install(self):
self.install_confirmed = True
@ -417,8 +393,7 @@ class Subiquity(Application):
self.ui.set_body(self.controllers.InstallProgress.progress_view)
async def _apply(self, controller):
with controller.context.child("apply_autoinstall_config"):
await controller.apply_autoinstall_config()
await controller.apply_autoinstall_config()
controller.autoinstall_applied = True
controller.configured()
self.next_screen()

View File

@ -96,11 +96,13 @@ class ProgressView(BaseView):
def event_start(self, context, message):
self.event_finish(context.parent)
walker = self.event_listbox.base_widget.body
indent = ' ' * (context.full_name().count('/') - 2)
indent = context.full_name().count('/') - 2
if context.get('is-install-context'):
indent -= 1
spinner = Spinner(self.controller.app.aio_loop)
spinner.start()
new_line = Columns([
('pack', Text(indent + message)),
('pack', Text(' ' * indent + message)),
('pack', spinner),
], dividechars=1)
self.ongoing[context] = len(walker)

View File

@ -109,3 +109,20 @@ class Context:
return c.data[key]
c = c.parent
return default
def with_context(name=None, description="", **context_kw):
def decorate(meth):
nonlocal name
if name is None:
name = meth.__name__
async def decorated(self, context=None, *args, **kw):
if context is None:
context = self.context
manager = context.child(
name, description=description.format(**kw), **context_kw)
with manager as subcontext:
await meth(self, subcontext, *args, **kw)
return decorated
return decorate

View File

@ -23,6 +23,7 @@ import yaml
from probert.network import IFF_UP, NetworkEventReceiver
from subiquitycore.async_helpers import SingleInstanceTask
from subiquitycore.context import with_context
from subiquitycore.controller import BaseController
from subiquitycore.file_util import write_file
from subiquitycore.models.network import (
@ -315,8 +316,8 @@ class NetworkController(BaseController):
netplan_config_file_name = '00-snapd-config.yaml'
return os.path.join(self.root, 'etc/netplan', netplan_config_file_name)
def apply_config(self, silent=False):
self.apply_config_task.start_sync(silent)
def apply_config(self, context=None, silent=False):
self.apply_config_task.start_sync(context, silent=silent)
async def _down_devs(self, devs):
for dev in devs:
@ -358,115 +359,115 @@ class NetworkController(BaseController):
self.parse_netplan_configs()
async def _apply_config(self, silent):
with self.context.child(
"apply_config", "silent={}".format(silent), level="INFO"):
devs_to_delete = []
devs_to_down = []
dhcp_device_versions = []
dhcp_events = set()
for dev in self.model.get_all_netdevs(include_deleted=True):
dev.dhcp_events = {}
for v in 4, 6:
if dev.dhcp_enabled(v):
if not silent:
dev.set_dhcp_state(v, "PENDING")
self.network_event_receiver.update_link(
dev.ifindex)
else:
dev.set_dhcp_state(v, "RECONFIGURE")
dev.dhcp_events[v] = e = asyncio.Event()
dhcp_events.add(e)
if dev.info is None:
continue
if dev.config != self.model.config.config_for_device(dev.info):
if dev.is_virtual:
devs_to_delete.append(dev)
@with_context(
name="apply_config", description="silent={silent}", level="INFO")
async def _apply_config(self, context, *, silent):
devs_to_delete = []
devs_to_down = []
dhcp_device_versions = []
dhcp_events = set()
for dev in self.model.get_all_netdevs(include_deleted=True):
dev.dhcp_events = {}
for v in 4, 6:
if dev.dhcp_enabled(v):
if not silent:
dev.set_dhcp_state(v, "PENDING")
self.network_event_receiver.update_link(
dev.ifindex)
else:
devs_to_down.append(dev)
self._write_config()
if not silent and self.view:
self.view.show_apply_spinner()
try:
def error(stage):
if not silent and self.view:
self.view.show_network_error(stage)
if self.opts.dry_run:
delay = 1/self.app.scale_factor
await arun_command(['sleep', str(delay)])
if os.path.exists('/lib/netplan/generate'):
# If netplan appears to be installed, run generate to
# at least test that what we wrote is acceptable to
# netplan.
await arun_command(
['netplan', 'generate', '--root', self.root],
check=True)
dev.set_dhcp_state(v, "RECONFIGURE")
dev.dhcp_events[v] = e = asyncio.Event()
dhcp_events.add(e)
if dev.info is None:
continue
if dev.config != self.model.config.config_for_device(dev.info):
if dev.is_virtual:
devs_to_delete.append(dev)
else:
if devs_to_down or devs_to_delete:
try:
await arun_command(
['systemctl', 'mask', '--runtime',
'systemd-networkd.service',
'systemd-networkd.socket'],
check=True)
await arun_command(
['systemctl', 'stop',
'systemd-networkd.service',
'systemd-networkd.socket'],
check=True)
except subprocess.CalledProcessError:
error("stop-networkd")
raise
if devs_to_down:
await self._down_devs(devs_to_down)
if devs_to_delete:
await self._delete_devs(devs_to_delete)
if devs_to_down or devs_to_delete:
devs_to_down.append(dev)
self._write_config()
if not silent and self.view:
self.view.show_apply_spinner()
try:
def error(stage):
if not silent and self.view:
self.view.show_network_error(stage)
if self.opts.dry_run:
delay = 1/self.app.scale_factor
await arun_command(['sleep', str(delay)])
if os.path.exists('/lib/netplan/generate'):
# If netplan appears to be installed, run generate to
# at least test that what we wrote is acceptable to
# netplan.
await arun_command(
['netplan', 'generate', '--root', self.root],
check=True)
else:
if devs_to_down or devs_to_delete:
try:
await arun_command(
['systemctl', 'unmask', '--runtime',
['systemctl', 'mask', '--runtime',
'systemd-networkd.service',
'systemd-networkd.socket'],
check=True)
try:
await arun_command(['netplan', 'apply'], check=True)
except subprocess.CalledProcessError:
error("apply")
raise
if devs_to_down or devs_to_delete:
# It's probably running already, but just in case.
await arun_command(
['systemctl', 'start', 'systemd-networkd.socket'],
check=False)
finally:
if not silent and self.view:
self.view.hide_apply_spinner()
['systemctl', 'stop',
'systemd-networkd.service',
'systemd-networkd.socket'],
check=True)
except subprocess.CalledProcessError:
error("stop-networkd")
raise
if devs_to_down:
await self._down_devs(devs_to_down)
if devs_to_delete:
await self._delete_devs(devs_to_delete)
if devs_to_down or devs_to_delete:
await arun_command(
['systemctl', 'unmask', '--runtime',
'systemd-networkd.service',
'systemd-networkd.socket'],
check=True)
try:
await arun_command(['netplan', 'apply'], check=True)
except subprocess.CalledProcessError:
error("apply")
raise
if devs_to_down or devs_to_delete:
# It's probably running already, but just in case.
await arun_command(
['systemctl', 'start', 'systemd-networkd.socket'],
check=False)
finally:
if not silent and self.view:
self.view.hide_apply_spinner()
if self.answers.get('accept-default', False):
self.done()
elif self.answers.get('actions', False):
actions = self.answers['actions']
self.answers.clear()
self._run_iterator(self._run_actions(actions))
if self.answers.get('accept-default', False):
self.done()
elif self.answers.get('actions', False):
actions = self.answers['actions']
self.answers.clear()
self._run_iterator(self._run_actions(actions))
if not dhcp_events:
return
if not dhcp_events:
return
try:
await asyncio.wait_for(
asyncio.wait({e.wait() for e in dhcp_events}),
10)
except asyncio.TimeoutError:
pass
try:
await asyncio.wait_for(
asyncio.wait({e.wait() for e in dhcp_events}),
10)
except asyncio.TimeoutError:
pass
for dev, v in dhcp_device_versions:
dev.dhcp_events = {}
if not dev.dhcp_addresses()[v]:
dev.set_dhcp_state(v, "TIMEDOUT")
self.network_event_receiver.update_link(dev.ifindex)
for dev, v in dhcp_device_versions:
dev.dhcp_events = {}
if not dev.dhcp_addresses()[v]:
dev.set_dhcp_state(v, "TIMEDOUT")
self.network_event_receiver.update_link(dev.ifindex)
def add_vlan(self, device, vlan):
return self.model.new_vlan(device, vlan)