1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
"""
Test that disk setup can run successfully on a mounted partition when
partprobe is being used.
lp-1920939
"""
import json
import os
import pytest
from uuid import uuid4
from pycloudlib.lxd.instance import LXDInstance
from cloudinit.subp import subp
from tests.integration_tests.instances import IntegrationInstance
DISK_PATH = '/tmp/test_disk_setup_{}'.format(uuid4())
def setup_and_mount_lxd_disk(instance: LXDInstance):
subp('lxc config device add {} test-disk-setup-disk disk source={}'.format(
instance.name, DISK_PATH).split())
@pytest.yield_fixture
def create_disk():
# 640k should be enough for anybody
subp('dd if=/dev/zero of={} bs=1k count=640'.format(DISK_PATH).split())
yield
os.remove(DISK_PATH)
USERDATA = """\
#cloud-config
disk_setup:
/dev/sdb:
table_type: mbr
layout: [50, 50]
overwrite: True
fs_setup:
- label: test
device: /dev/sdb1
filesystem: ext4
- label: test2
device: /dev/sdb2
filesystem: ext4
mounts:
- ["/dev/sdb1", "/mnt1"]
- ["/dev/sdb2", "/mnt2"]
"""
UPDATED_USERDATA = """\
#cloud-config
disk_setup:
/dev/sdb:
table_type: mbr
layout: [100]
overwrite: True
fs_setup:
- label: test3
device: /dev/sdb1
filesystem: ext4
mounts:
- ["/dev/sdb1", "/mnt3"]
"""
def _verify_first_disk_setup(client, log):
assert 'Traceback' not in log
assert 'WARN' not in log
lsblk = json.loads(client.execute('lsblk --json'))
sdb = [x for x in lsblk['blockdevices'] if x['name'] == 'sdb'][0]
assert len(sdb['children']) == 2
assert sdb['children'][0]['name'] == 'sdb1'
assert sdb['children'][0]['mountpoint'] == '/mnt1'
assert sdb['children'][1]['name'] == 'sdb2'
assert sdb['children'][1]['mountpoint'] == '/mnt2'
@pytest.mark.user_data(USERDATA)
@pytest.mark.lxd_setup.with_args(setup_and_mount_lxd_disk)
@pytest.mark.ubuntu
@pytest.mark.lxd_vm
# Not bionic or xenial because the LXD agent gets in the way of us
# changing the userdata
@pytest.mark.not_bionic
@pytest.mark.not_xenial
def test_disk_setup_when_mounted(create_disk, client: IntegrationInstance):
"""Test lp-1920939.
We insert an extra disk into our VM, format it to have two partitions,
modify our cloud config to mount devices before disk setup, and modify
our userdata to setup a single partition on the disk.
This allows cloud-init to attempt disk setup on a mounted partition.
When blockdev is in use, it will fail with
"blockdev: ioctl error on BLKRRPART: Device or resource busy" along
with a warning and a traceback. When partprobe is in use, everything
should work successfully.
"""
log = client.read_from_file('/var/log/cloud-init.log')
_verify_first_disk_setup(client, log)
# Update our userdata and cloud.cfg to mount then perform new disk setup
client.write_to_file(
'/var/lib/cloud/seed/nocloud-net/user-data',
UPDATED_USERDATA
)
client.execute("sed -i 's/write-files/write-files\\n - mounts/' "
"/etc/cloud/cloud.cfg")
client.execute('cloud-init clean --logs')
client.restart()
# Assert new setup works as expected
assert 'Traceback' not in log
assert 'WARN' not in log
lsblk = json.loads(client.execute('lsblk --json'))
sdb = [x for x in lsblk['blockdevices'] if x['name'] == 'sdb'][0]
assert len(sdb['children']) == 1
assert sdb['children'][0]['name'] == 'sdb1'
assert sdb['children'][0]['mountpoint'] == '/mnt3'
@pytest.mark.user_data(USERDATA)
@pytest.mark.lxd_setup.with_args(setup_and_mount_lxd_disk)
@pytest.mark.ubuntu
@pytest.mark.lxd_vm
def test_disk_setup_no_partprobe(create_disk, client: IntegrationInstance):
"""Ensure disk setup still works as expected without partprobe."""
# We can't do this part in a bootcmd because the path has already
# been found by the time we get to the bootcmd
client.execute('rm $(which partprobe)')
client.execute('cloud-init clean --logs')
client.restart()
log = client.read_from_file('/var/log/cloud-init.log')
_verify_first_disk_setup(client, log)
assert 'partprobe' not in log
|