Merge pull request #1697 from mwhudson/reset-only
support for creating only a reset partition
This commit is contained in:
commit
5ba9e524d8
|
@ -0,0 +1,11 @@
|
|||
version: 1
|
||||
identity:
|
||||
realname: ''
|
||||
username: ubuntu
|
||||
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
|
||||
hostname: ubuntu
|
||||
storage:
|
||||
layout:
|
||||
name: direct
|
||||
reset-partition: yes
|
||||
reset-partition-only: yes
|
|
@ -31,16 +31,25 @@ validate () {
|
|||
cfgs+=("$cfg")
|
||||
fi
|
||||
done
|
||||
python3 scripts/validate-yaml.py "${cfgs[@]}"
|
||||
if [ ! -e $tmpdir/subiquity-client-debug.log ] || [ ! -e $tmpdir/subiquity-server-debug.log ]; then
|
||||
echo "log file not created"
|
||||
exit 1
|
||||
fi
|
||||
python3 scripts/validate-autoinstall-user-data.py < $tmpdir/var/log/installer/autoinstall-user-data
|
||||
if grep passw0rd $tmpdir/subiquity-client-debug.log $tmpdir/subiquity-server-debug.log | grep -v "Loaded answers" | grep -v "answers_action"; then
|
||||
echo "password leaked into log file"
|
||||
exit 1
|
||||
fi
|
||||
opt=
|
||||
[ $# -gt 1 ] && opt="$2"
|
||||
if [ $opt = reset-only ]; then
|
||||
python3 scripts/validate-yaml.py --no-root-mount "${cfgs[@]}"
|
||||
else
|
||||
python3 scripts/validate-yaml.py "${cfgs[@]}"
|
||||
fi
|
||||
if [ ! -e $tmpdir/subiquity-client-debug.log ] || [ ! -e $tmpdir/subiquity-server-debug.log ]; then
|
||||
echo "log file not created"
|
||||
exit 1
|
||||
fi
|
||||
if [ $opt = reset-only ]; then
|
||||
return
|
||||
fi
|
||||
python3 scripts/validate-autoinstall-user-data.py < $tmpdir/var/log/installer/autoinstall-user-data
|
||||
netplan generate --root $tmpdir
|
||||
elif [ "${mode}" = "system_setup" ]; then
|
||||
setup_mode="$2"
|
||||
|
@ -254,6 +263,17 @@ python3 scripts/check-yaml-fields.py "$tmpdir"/var/log/installer/autoinstall-use
|
|||
'autoinstall.source.id="ubuntu-server-minimal"'
|
||||
grep -q 'finish: subiquity/Install/install/postinstall/run_unattended_upgrades: SUCCESS: downloading and installing security updates' $tmpdir/subiquity-server-debug.log
|
||||
|
||||
clean
|
||||
LANG=C.UTF-8 timeout --foreground 60 \
|
||||
python3 -m subiquity.cmd.tui \
|
||||
--dry-run \
|
||||
--output-base "$tmpdir" \
|
||||
--machine-config examples/simple.json \
|
||||
--autoinstall examples/autoinstall-reset-only.yaml \
|
||||
--kernel-cmdline autoinstall \
|
||||
--source-catalog examples/install-sources.yaml
|
||||
validate install reset-only
|
||||
|
||||
# The OOBE doesn't exist in WSL < 20.04
|
||||
if [ "${RELEASE%.*}" -ge 20 ]; then
|
||||
# Test TCP connectivity (system_setup only)
|
||||
|
|
|
@ -75,11 +75,15 @@ class StorageChecker:
|
|||
assert '/' in self.path_to_mount
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
storage_checker = StorageChecker()
|
||||
|
||||
root_mount = True
|
||||
|
||||
if sys.argv[1:2] == ['--no-root-mount']:
|
||||
root_mount = False
|
||||
sys.argv.pop(1)
|
||||
|
||||
actions = []
|
||||
for path in sys.argv[1:]:
|
||||
config = yaml.safe_load(open(path))
|
||||
|
@ -92,7 +96,8 @@ def main():
|
|||
print('checking {} failed'.format(action))
|
||||
raise
|
||||
|
||||
storage_checker.final_checks()
|
||||
if root_mount:
|
||||
storage_checker.final_checks()
|
||||
|
||||
|
||||
main()
|
||||
|
|
|
@ -125,6 +125,9 @@ system_non_gpt_text = _(
|
|||
)
|
||||
|
||||
|
||||
DRY_RUN_RESET_SIZE = 500*MiB
|
||||
|
||||
|
||||
class NoSnapdSystemsOnSource(Exception):
|
||||
pass
|
||||
|
||||
|
@ -243,6 +246,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
# this variable. It will be picked up on next reset.
|
||||
self.queued_probe_data: Optional[Dict[str, Any]] = None
|
||||
self.reset_partition: Optional[ModelPartition] = None
|
||||
self.reset_partition_only: bool = False
|
||||
|
||||
def is_core_boot_classic(self):
|
||||
return self._info.is_core_boot_classic()
|
||||
|
@ -413,6 +417,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
},
|
||||
}
|
||||
await self.convert_autoinstall_config(context=context)
|
||||
if self.reset_partition_only:
|
||||
return
|
||||
if not self.model.is_root_mounted():
|
||||
raise Exception("autoinstall config did not mount root")
|
||||
if self.model.needs_bootloader_partition():
|
||||
|
@ -535,7 +541,11 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
raise Exception(
|
||||
"could not find variation for {}".format(capability))
|
||||
|
||||
async def guided(self, choice: GuidedChoiceV2):
|
||||
async def guided(
|
||||
self,
|
||||
choice: GuidedChoiceV2,
|
||||
reset_partition_only: bool = False
|
||||
) -> None:
|
||||
self.model.guided_configuration = choice
|
||||
|
||||
self.set_info_for_capability(choice.capability)
|
||||
|
@ -558,14 +568,22 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
raise Exception('failed to locate gap after adding boot')
|
||||
|
||||
if choice.reset_partition:
|
||||
cp = await arun_command(['du', '-sb', '/cdrom'])
|
||||
reset_size = int(cp.stdout.strip().split()[0])
|
||||
reset_size = align_up(int(reset_size * 1.10), 256 * MiB)
|
||||
if self.app.opts.dry_run:
|
||||
reset_size = DRY_RUN_RESET_SIZE
|
||||
else:
|
||||
cp = await arun_command(['du', '-sb', '/cdrom'])
|
||||
reset_size = int(cp.stdout.strip().split()[0])
|
||||
reset_size = align_up(int(reset_size * 1.10), 256 * MiB)
|
||||
reset_gap, gap = gap.split(reset_size)
|
||||
self.reset_partition = self.create_partition(
|
||||
device=reset_gap.device, gap=reset_gap,
|
||||
spec={'fstype': 'fat32'}, flag='msftres')
|
||||
# Should probably set some kind of flag on reset_partition
|
||||
self.reset_partition_only = reset_partition_only
|
||||
if reset_partition_only:
|
||||
for mount in self.model._all(type='mount'):
|
||||
self.delete_mount(mount)
|
||||
self.model.target = self.app.base_model.target = None
|
||||
return
|
||||
|
||||
if choice.capability.is_lvm():
|
||||
self.guided_lvm(gap, choice)
|
||||
|
@ -1223,7 +1241,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
GuidedChoiceV2(
|
||||
target=target, capability=capability,
|
||||
password=password, sizing_policy=sizing_policy,
|
||||
reset_partition=layout.get('reset-partition', False)))
|
||||
reset_partition=layout.get('reset-partition', False)),
|
||||
reset_partition_only=layout.get('reset-partition-only', False))
|
||||
|
||||
def validate_layout_mode(self, mode):
|
||||
if mode not in ('reformat_disk', 'use_gap'):
|
||||
|
@ -1315,6 +1334,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
return r
|
||||
|
||||
async def _pre_shutdown(self):
|
||||
await self.app.command_runner.run(['umount', '--recursive', '/target'])
|
||||
if not self.reset_partition_only:
|
||||
await self.app.command_runner.run(
|
||||
['umount', '--recursive', '/target'])
|
||||
for pool in self.model._all(type='zpool'):
|
||||
await pool.pre_shutdown(self.app.command_runner)
|
||||
|
|
|
@ -259,7 +259,14 @@ class InstallController(SubiquityController):
|
|||
|
||||
await run_curtin_step(name="initial", stages=[], step_config={})
|
||||
|
||||
if fs_controller.is_core_boot_classic():
|
||||
if fs_controller.reset_partition_only:
|
||||
await run_curtin_step(
|
||||
name="partitioning", stages=["partitioning"],
|
||||
step_config=self.filesystem_config(
|
||||
device_map_path=logs_dir / "device-map.json",
|
||||
),
|
||||
)
|
||||
elif fs_controller.is_core_boot_classic():
|
||||
await run_curtin_step(
|
||||
name="partitioning", stages=["partitioning"],
|
||||
step_config=self.filesystem_config(
|
||||
|
@ -349,24 +356,29 @@ class InstallController(SubiquityController):
|
|||
|
||||
self.app.update_state(ApplicationState.RUNNING)
|
||||
|
||||
for_install_path = await self.configure_apt(context=context)
|
||||
if not self.app.controllers.Filesystem.reset_partition_only:
|
||||
for_install_path = await self.configure_apt(context=context)
|
||||
|
||||
await self.app.hub.abroadcast(InstallerChannels.APT_CONFIGURED)
|
||||
await self.app.hub.abroadcast(InstallerChannels.APT_CONFIGURED)
|
||||
|
||||
if os.path.exists(self.model.target):
|
||||
await self.unmount_target(
|
||||
context=context, target=self.model.target)
|
||||
if os.path.exists(self.model.target):
|
||||
await self.unmount_target(
|
||||
context=context, target=self.model.target)
|
||||
else:
|
||||
for_install_path = ''
|
||||
|
||||
await self.curtin_install(
|
||||
context=context, source='cp://' + for_install_path)
|
||||
|
||||
self.app.update_state(ApplicationState.WAITING)
|
||||
if not self.app.controllers.Filesystem.reset_partition_only:
|
||||
|
||||
await self.model.wait_postinstall()
|
||||
self.app.update_state(ApplicationState.WAITING)
|
||||
|
||||
self.app.update_state(ApplicationState.RUNNING)
|
||||
await self.model.wait_postinstall()
|
||||
|
||||
await self.postinstall(context=context)
|
||||
self.app.update_state(ApplicationState.RUNNING)
|
||||
|
||||
await self.postinstall(context=context)
|
||||
|
||||
self.app.update_state(ApplicationState.DONE)
|
||||
except Exception:
|
||||
|
|
|
@ -96,6 +96,8 @@ class ShutdownController(SubiquityController):
|
|||
async def copy_logs_to_target(self, context):
|
||||
if self.opts.dry_run and 'copy-logs-fail' in self.app.debug_flags:
|
||||
raise PermissionError()
|
||||
if self.app.controllers.Filesystem.reset_partition_only:
|
||||
return
|
||||
target_logs = os.path.join(
|
||||
self.app.base_model.target, 'var/log/installer')
|
||||
if self.opts.dry_run:
|
||||
|
|
|
@ -54,6 +54,7 @@ from subiquity.models.tests.test_filesystem import (
|
|||
)
|
||||
from subiquity.server import snapdapi
|
||||
from subiquity.server.controllers.filesystem import (
|
||||
DRY_RUN_RESET_SIZE,
|
||||
FilesystemController,
|
||||
VariationInfo,
|
||||
)
|
||||
|
@ -340,6 +341,36 @@ class TestGuided(IsolatedAsyncioTestCase):
|
|||
self.assertFalse(d1p2.preserve)
|
||||
self.assertIsNone(gaps.largest_gap(self.d1))
|
||||
|
||||
async def test_guided_reset_partition(self):
|
||||
await self._guided_setup(Bootloader.UEFI, 'gpt')
|
||||
target = GuidedStorageTargetReformat(
|
||||
disk_id=self.d1.id, allowed=default_capabilities)
|
||||
await self.controller.guided(
|
||||
GuidedChoiceV2(
|
||||
target=target,
|
||||
capability=GuidedCapability.DIRECT,
|
||||
reset_partition=True))
|
||||
[d1p1, d1p2, d1p3] = self.d1.partitions()
|
||||
self.assertEqual('/boot/efi', d1p1.mount)
|
||||
self.assertEqual(None, d1p2.mount)
|
||||
self.assertEqual(DRY_RUN_RESET_SIZE, d1p2.size)
|
||||
self.assertEqual('/', d1p3.mount)
|
||||
|
||||
async def test_guided_reset_partition_only(self):
|
||||
await self._guided_setup(Bootloader.UEFI, 'gpt')
|
||||
target = GuidedStorageTargetReformat(
|
||||
disk_id=self.d1.id, allowed=default_capabilities)
|
||||
await self.controller.guided(
|
||||
GuidedChoiceV2(
|
||||
target=target,
|
||||
capability=GuidedCapability.DIRECT,
|
||||
reset_partition=True),
|
||||
reset_partition_only=True)
|
||||
[d1p1, d1p2] = self.d1.partitions()
|
||||
self.assertEqual(None, d1p1.mount)
|
||||
self.assertEqual(None, d1p2.mount)
|
||||
self.assertEqual(DRY_RUN_RESET_SIZE, d1p2.size)
|
||||
|
||||
async def test_guided_direct_BIOS_MSDOS(self):
|
||||
await self._guided_setup(Bootloader.BIOS, 'msdos')
|
||||
target = GuidedStorageTargetReformat(
|
||||
|
|
Loading…
Reference in New Issue