Merge ~smoser/curtin:feature/add-fstest-battery into curtin:master

Proposed by Scott Moser
Status: Merged
Approved by: Scott Moser
Approved revision: ece93d5d40c56f5e07c3575f134de0e9ae915330
Merge reported by: Scott Moser
Merged at revision: 5b7ca31e32eb47eedf2862e7b323dbb9ecb2a781
Proposed branch: ~smoser/curtin:feature/add-fstest-battery
Merge into: curtin:master
Diff against target: 326 lines (+285/-1)
4 files modified
curtin/block/__init__.py (+4/-1)
curtin/block/mkfs.py (+1/-0)
examples/tests/filesystem_battery.yaml (+101/-0)
tests/vmtests/test_fs_battery.py (+179/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Ryan Harper (community) Approve
Review via email: mp+337244@code.launchpad.net

Commit message

vmtest: Add Filesystem Battery test.

This test exercises filesystem creation, mount and unmount of all
supported filesystem types.

Also here is a fix for jfs filesystem creation. mkfs_jfs requires a '-q'
argument to force/quiet. Otherwise it will prompt to ask if you
really want to do that.

Description of the change

see commit message

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

I still need to test that the things are mountable. So far I've only been through the installation of Bionic.

Revision history for this message
Ryan Harper (raharper) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Scott Moser (smoser) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

And it all passes? Nice work. One comment on where to store the blkid output parser.

Revision history for this message
Scott Moser (smoser) wrote :

its not completely generic blkid parser as it is.
but yes, i thought about putting that into the base class and just always collecting that info.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Let's confirm this passed on Jenkins and I'm +1

review: Approve
Revision history for this message
Scott Moser (smoser) wrote :

On diglet, I did:
 git

$ git rev-parse HEAD
ece93d5d40c56f5e07c3575f134de0e9ae915330

# full output: http://paste.ubuntu.com/p/GFZpHtjZ5k/
$ ./tools/jenkins-runner -p4 tests/vmtests/test_fs_battery.py
CURTIN_VMTEST_IMAGE_SYNC=0
CURTIN_VMTEST_ISCSI_PORTAL=10.245.168.20:5336
CURTIN_VMTEST_KEEP_DATA_FAIL=logs,collect
CURTIN_VMTEST_KEEP_DATA_PASS=logs,collect
CURTIN_VMTEST_LOG=/srv/smoser/curtin/curtin/output/debug.log
CURTIN_VMTEST_PARALLEL=4
CURTIN_VMTEST_REUSE_TOPDIR=0
CURTIN_VMTEST_TAR_DISKS=0
CURTIN_VMTEST_TOPDIR=/srv/smoser/curtin/curtin/output
TGT_IPC_SOCKET=/srv/smoser/curtin/curtin/output/tgt.d/socket
TGT_LOG_D=/srv/smoser/curtin/curtin/output/tgt.d
TGT_PID=17545
TGT_PORTAL=10.245.168.20:5336
http_proxy=
https_proxy=
no_proxy=
Quering synced ephemeral images/kernels in /srv/images
======================================================================================
 Release Codename ImageDate Arch/SubArch Path
--------------------------------------------------------------------------------------
   12.04 precise 20170424 amd64/hwe-t precise/amd64/20170424/root-image.gz
   14.04 trusty 20180302 amd64/hwe-t trusty/amd64/20180302/squashfs
   14.04 trusty 20180302 amd64/hwe-x trusty/amd64/20180302/squashfs
   16.04 xenial 20180306 amd64/ga-16.04 xenial/amd64/20180306/squashfs
   16.04 xenial 20180306 amd64/hwe-16.04 xenial/amd64/20180306/squashfs
   16.04 xenial 20180306 amd64/hwe-16.04-edge xenial/amd64/20180306/squashfs
   17.04 zesty 20171219 amd64/ga-17.04 zesty/amd64/20171219/squashfs
   17.10 artful 20180303 amd64/ga-17.10 artful/amd64/20180303/squashfs
   18.04 bionic 20180224 amd64/ga-18.04 bionic/amd64/20180224/squashfs
======================================================================================

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/block/__init__.py b/curtin/block/__init__.py
2index a82ed57..50e953e 100644
3--- a/curtin/block/__init__.py
4+++ b/curtin/block/__init__.py
5@@ -1051,13 +1051,16 @@ def detect_required_packages_mapping():
6 'ext2': ['e2fsprogs'],
7 'ext3': ['e2fsprogs'],
8 'ext4': ['e2fsprogs'],
9+ 'jfs': ['jfsutils'],
10 'lvm_partition': ['lvm2'],
11 'lvm_volgroup': ['lvm2'],
12+ 'ntfs': ['ntfs-3g'],
13 'raid': ['mdadm'],
14+ 'reiserfs': ['reiserfsprogs'],
15 'xfs': ['xfsprogs'],
16+ 'zfsroot': ['zfsutils-linux', 'zfs-initramfs'],
17 'zfs': ['zfsutils-linux', 'zfs-initramfs'],
18 'zpool': ['zfsutils-linux', 'zfs-initramfs'],
19- 'zfsroot': ['zfsutils-linux', 'zfs-initramfs'],
20 },
21 },
22 }
23diff --git a/curtin/block/mkfs.py b/curtin/block/mkfs.py
24index 5cdb87c..a199d05 100644
25--- a/curtin/block/mkfs.py
26+++ b/curtin/block/mkfs.py
27@@ -54,6 +54,7 @@ family_flag_mappings = {
28 "force": {"btrfs": "--force",
29 "ext": "-F",
30 "fat": "-I",
31+ "jfs": "-q",
32 "ntfs": "--force",
33 "reiserfs": "-f",
34 "swap": "--force",
35diff --git a/examples/tests/filesystem_battery.yaml b/examples/tests/filesystem_battery.yaml
36new file mode 100644
37index 0000000..ba4fcac
38--- /dev/null
39+++ b/examples/tests/filesystem_battery.yaml
40@@ -0,0 +1,101 @@
41+showtrace: true
42+early_commands:
43+ "00": [apt-get, update, -qy]
44+ "01": [apt-get, install, -qy, --no-install-recommends,
45+ ntfs-3g, jfsutils, reiserfsprogs]
46+storage:
47+ version: 1
48+ config:
49+ - id: disk1
50+ type: disk
51+ ptable: msdos
52+ model: QEMU HARDDISK
53+ serial: disk-a
54+ wipe: superblock
55+ grub_device: true
56+ - id: disk1p1
57+ type: partition
58+ number: 1
59+ size: 3GB
60+ device: disk1
61+ flag: boot
62+ - id: disk1p1_fs
63+ type: format
64+ fstype: ext4
65+ volume: disk1p1
66+ label: 'cloudimg-rootfs'
67+ - id: disk1p1_mount
68+ type: mount
69+ path: /
70+ device: disk1p1_fs
71+ - id: disk2
72+ type: disk
73+ serial: fsbattery
74+ wipe: superblock
75+ ptable: gpt
76+ - {id: d2p01, number: 1, device: disk2, type: partition, size: 500M}
77+ - {id: d2p02, number: 2, device: disk2, type: partition, size: 500M}
78+ - {id: d2p03, number: 3, device: disk2, type: partition, size: 500M}
79+ - {id: d2p04, number: 4, device: disk2, type: partition, size: 500M}
80+ - {id: d2p05, number: 5, device: disk2, type: partition, size: 500M}
81+ - {id: d2p06, number: 6, device: disk2, type: partition, size: 500M}
82+ - {id: d2p07, number: 7, device: disk2, type: partition, size: 500M}
83+ - {id: d2p08, number: 8, device: disk2, type: partition, size: 500M}
84+ - {id: d2p09, number: 9, device: disk2, type: partition, size: 500M}
85+ - {id: d2p10, number: 10, device: disk2, type: partition, size: 500M}
86+ - id: fs01
87+ type: format
88+ fstype: btrfs
89+ label: mybtrfs
90+ volume: d2p01
91+ uuid: 8946d6ad-1e5f-4609-924c-4a39b6b561c9
92+ - id: fs02
93+ type: format
94+ fstype: ext2
95+ label: myext2
96+ volume: d2p02
97+ uuid: 5d60e5e8-0c41-11e8-a664-525400123456
98+ - id: fs03
99+ type: format
100+ fstype: ext3
101+ label: myext3
102+ volume: d2p03
103+ uuid: 5d7f4d30-0c41-11e8-a664-525400123456
104+ - id: fs04
105+ type: format
106+ fstype: ext4
107+ label: myext4
108+ volume: d2p04
109+ uuid: 5da136b6-0c41-11e8-a664-525400123456
110+ - id: fs05
111+ type: format
112+ fstype: fat16
113+ label: myvfat16
114+ volume: d2p05
115+ - id: fs06
116+ type: format
117+ fstype: fat32
118+ label: myvfat32
119+ volume: d2p06
120+ - id: fs07
121+ type: format
122+ fstype: jfs
123+ label: myjfs
124+ volume: d2p07
125+ - id: fs08
126+ type: format
127+ fstype: ntfs
128+ label: myntfs
129+ volume: d2p08
130+ - id: fs09
131+ type: format
132+ fstype: reiserfs
133+ label: myreiserfs
134+ volume: d2p09
135+ uuid: 5ed8308e-0c41-11e8-a664-525400123456
136+ - id: fs10
137+ type: format
138+ fstype: xfs
139+ label: myxfs
140+ volume: d2p10
141+ uuid: 9c537621-f2f4-4e24-a071-e05012a1a997
142diff --git a/tests/vmtests/test_fs_battery.py b/tests/vmtests/test_fs_battery.py
143new file mode 100644
144index 0000000..5798d48
145--- /dev/null
146+++ b/tests/vmtests/test_fs_battery.py
147@@ -0,0 +1,179 @@
148+# This file is part of curtin. See LICENSE file for copyright and license info.
149+
150+from . import VMBaseClass
151+from .releases import base_vm_classes as relbase
152+
153+from curtin import config
154+
155+import os
156+import textwrap
157+
158+
159+def _parse_blkid_output(content):
160+ """Parse the output of the 'blkid' calls in collect_script.
161+
162+ Input is groups of lines. Each line is key=value. Each group
163+ has the first line with key DEVNAME and last line key RESULT.
164+
165+ returned value is a dictionary by shortened devname like:.
166+ {'part1': {'devname': 'part1', 'label': '...'}}"""
167+ def _record(lines):
168+ record = {}
169+ for line in lines:
170+ key, _, val = line.partition("=")
171+ if key == 'DEVNAME':
172+ bname = os.path.basename(val)
173+ # bname is 'virtio-fsbattery-partX'. get just 'partX'
174+ record[key.lower()] = bname.rpartition("-")[2]
175+ elif key in ('RESULT', 'LABEL', 'UUID', 'TYPE'):
176+ record[key.lower()] = val
177+ return record
178+
179+ lines = []
180+ records = {}
181+ for line in content.splitlines():
182+ lines.append(line)
183+ if line.startswith("RESULT"):
184+ r = _record(lines)
185+ records[r['devname']] = r
186+ lines = []
187+
188+ return records
189+
190+
191+class TestFsBattery(VMBaseClass):
192+ interactive = False
193+ conf_file = "examples/tests/filesystem_battery.yaml"
194+ extra_disks = ['20G']
195+ collect_scripts = VMBaseClass.collect_scripts + [textwrap.dedent("""
196+ cd OUTPUT_COLLECT_D
197+ blkid -o export > blkid.out
198+ cat /proc/mounts > proc_mounts
199+ cat /proc/partitions > proc_partitions
200+ find /etc/network/interfaces.d > find_interfacesd
201+ cat /proc/cmdline > cmdline
202+
203+ set +x
204+ serial="fsbattery"
205+ disk=$(echo /dev/disk/by-id/*-$serial)
206+ [ -b "$disk" ] || { echo "No disk with serial $serial." exit 1; }
207+
208+ # not all blkid versions output DEVNAME, so do it ourselves.
209+ blkid -o export "$disk" | grep -q DEVNAME= &&
210+ hasdev=true || hasdev=false
211+ for d in $disk-part*; do
212+ $hasdev || echo DEVNAME=$d
213+ blkid -o export "$d"
214+ echo RESULT=$?
215+ done > battery-blkid
216+
217+ mpbase=/tmp/mp;
218+ mkdir -p /tmp/mp
219+ for d in $disk-part*; do
220+ fstype=$(blkid -o export "$d" |
221+ awk -F= '$1 == "TYPE" { print $2 }')
222+ if [ -z "$fstype" ]; then
223+ msg="FAIL: blkid did not identify fstype"
224+ else
225+ mp="$mpbase/${d##*-}"
226+ mkdir "$mp"
227+ echo "${d##*-} $fstype" > "$mp.info"
228+ if out=$(mount -t "$fstype" "$d" "$mp" 2>&1); then
229+ msg="PASS"
230+ else
231+ rm -Rf "$mp.info" "$mp"
232+ msg="FAIL: mount $fstype failed $?: $out"
233+ fi
234+ fi
235+ echo "${d##*-} mount: $msg"
236+ done > battery-mount-umount
237+
238+ awk '$5 ~ mp { print $0 }' "mp=$mpbase/" \
239+ /proc/1/mountinfo > battery-mountinfo
240+
241+ for info in $mpbase/*.info; do
242+ read part fstype < "$info"
243+ mp="${info%.info}"
244+ out=$(umount "$mp" 2>&1) &&
245+ echo "$part umount: PASS" ||
246+ echo "$part umount: FAIL: $out"
247+ done >> battery-mount-umount
248+ """)]
249+
250+ def get_fs_entries(self):
251+ """Return a dictionary of fs entires in config by 'partX'."""
252+ stgcfg = config.load_config(self.conf_file)['storage']['config']
253+ fs_entries = {}
254+ for entry in stgcfg:
255+ if not entry['id'].startswith("fs"):
256+ continue
257+ part = "part%d" % int(entry['id'][2:])
258+ fs_entries[part] = entry.copy()
259+ return fs_entries
260+
261+ def test_blkid_output(self):
262+ """Check the recorded output of 'blkid -o export' on each partition.
263+
264+ parse parse the 'battery-blkid' collected file, and compare it
265+ to expected output from reading the storage config."""
266+ results = _parse_blkid_output(self.load_collect_file("battery-blkid"))
267+
268+ # tools for these types do not support providing uuid.
269+ no_uuid_types = ['vfat', 'jfs', 'fat16', 'fat32', 'ntfs']
270+ if self.release in ('trusty'):
271+ no_uuid_types += ['btrfs', 'xfs']
272+
273+ for k, v in results.items():
274+ if v['type'] in no_uuid_types:
275+ del v['uuid']
276+
277+ # these curtin "types" show in blkid output differently.
278+ type2blkid = {'fat32': 'vfat', 'fat16': 'vfat'}
279+ expected = {}
280+ for part, entry in self.get_fs_entries().items():
281+ record = {
282+ 'devname': part,
283+ 'label': entry['label'],
284+ 'type': type2blkid.get(entry['fstype'], entry['fstype']),
285+ 'result': "0",
286+ }
287+ if 'uuid' in entry and record['type'] not in no_uuid_types:
288+ record['uuid'] = entry['uuid']
289+ expected[record['devname']] = record
290+
291+ self.assertEqual(expected, results)
292+
293+ def test_mount_umount(self):
294+ """Check output of mount and unmount operations for each fs."""
295+ results = self.load_collect_file("battery-mount-umount").splitlines()
296+ entries = self.get_fs_entries()
297+ expected = (["%s mount: PASS" % k for k in entries] +
298+ ["%s umount: PASS" % k for k in entries])
299+ self.assertEqual(sorted(expected), sorted(results))
300+
301+
302+class TrustyTestFsBattery(relbase.trusty, TestFsBattery):
303+ __test__ = True
304+
305+
306+class TrustyHWEXTestFsBattery(relbase.trusty_hwe_x, TestFsBattery):
307+ __test__ = True
308+
309+
310+class XenialGATestFsBattery(relbase.xenial_ga, TestFsBattery):
311+ __test__ = True
312+
313+
314+class XenialHWETestFsBattery(relbase.xenial_hwe, TestFsBattery):
315+ __test__ = True
316+
317+
318+class XenialEdgeTestFsBattery(relbase.xenial_edge, TestFsBattery):
319+ __test__ = True
320+
321+
322+class BionicTestFsBattery(relbase.bionic, TestFsBattery):
323+ __test__ = True
324+
325+
326+# vi: ts=4 expandtab syntax=python

Subscribers

People subscribed via source and target branches