Merge ~mwhudson/curtin:pare-down-dasdview-parsing into curtin:master

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Paride Legovini
Approved revision: e9b3d0140d05f5a3f291ec99bd0d10d81969e4be
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~mwhudson/curtin:pare-down-dasdview-parsing
Merge into: curtin:master
Diff against target: 717 lines (+116/-400)
2 files modified
curtin/block/dasd.py (+43/-232)
tests/unittests/test_block_dasd.py (+73/-168)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Paride Legovini Approve
Review via email: mp+394207@code.launchpad.net

Commit message

simplify dasdview parsing code

Now that the only thing we need dasdview output for is to determine
the format type of the dasd (ldl vs cdl vs unformatted) we can make
a few things simpler.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paride Legovini (paride) wrote :

Jenkins lost connection with the worker node. Retriggering.

Caused: java.io.IOException: Backing channel 'JNLP4-connect connection from 10.247.8.42/10.247.8.42:49854' is disconnected.
 at hudson.remoting.RemoteInvocationHandler.channelOrFail(RemoteInvocationHandler.java:214)
 at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:283)
 at com.sun.proxy.$Proxy71.isAlive(Unknown Source)
 at hudson.Launcher$RemoteLauncher$ProcImpl.isAlive(Launcher.java:1150)
 at hudson.Launcher$RemoteLauncher$ProcImpl.join(Launcher.java:1142)
 at hudson.tasks.CommandInterpreter.join(CommandInterpreter.java:155)
 at hudson.tasks.CommandInterpreter.perform(CommandInterpreter.java:109)
 at hudson.tasks.CommandInterpreter.perform(CommandInterpreter.java:66)
 at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
 at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:741)
 at hudson.model.Build$BuildExecution.build(Build.java:206)
 at hudson.model.Build$BuildExecution.doRun(Build.java:163)
 at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:504)
 at hudson.model.Run.execute(Run.java:1818)
 at hudson.matrix.MatrixRun.run(MatrixRun.java:153)
 at hudson.model.ResourceController.execute(ResourceController.java:97)
 at hudson.model.Executor.run(Executor.java:429)

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
Dimitri John Ledkov (xnox) :
Revision history for this message
Paride Legovini (paride) wrote :

The arm64 Jenkins agent disconnected from the Jenkins master, and wouldn't reconnect.

While investigating I found out that after the data center move the arm64 node was pointing to a NTP server which became inaccessible, its clock went out of sync (several hours) and I *think* it was kicked from the worker nodes pool for this reason. Now it's online again, with a good NTP. I restarted the job.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Hudson-Doyle (mwhudson) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Paride Legovini (paride) wrote :

This LGTM from the code perspective, but I know very little about DASD.

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

Commit message lints:
- Line #2 has 83 too many characters. Line starts with: "Now that the only thing"...

review: Needs Fixing
Revision history for this message
Server Team CI bot (server-team-bot) :
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/dasd.py b/curtin/block/dasd.py
2index 7450300..0529c25 100644
3--- a/curtin/block/dasd.py
4+++ b/curtin/block/dasd.py
5@@ -1,13 +1,12 @@
6 # This file is part of curtin. See LICENSE file for copyright and license info.
7
8-import collections
9+import glob
10 import os
11+import re
12 import tempfile
13 from curtin import util
14 from curtin.log import LOG, logged_time
15
16-Dasdvalue = collections.namedtuple('Dasdvalue', ['hex', 'dec', 'txt'])
17-
18
19 class DasdPartition:
20 def __init__(self, table, device, start, end, length, id, system):
21@@ -116,196 +115,43 @@ def dasdinfo(device_id):
22 return util.load_shell_content(out)
23
24
25-def dasdview(devname):
26- ''' Run dasdview on devname and return dictionary of data.
27-
28- dasdview --extended has 3 sections
29- general (2:6), geometry (8:12), extended (14:)
30-
31- '''
32+def dasd_format(devname):
33+ """Return the format (ldl/cdl/not-formatted) of devname."""
34 if not os.path.exists(devname):
35 raise ValueError("Invalid dasd device name: '%s'" % devname)
36
37 out, err = util.subp(['dasdview', '--extended', devname], capture=True)
38
39- return _parse_dasdview(out)
40-
41-
42-def _parse_dasdview(view_output):
43- """ Parse dasdview --extended output into a dictionary
44-
45- Input:
46- --- general DASD information ---------------------------------------------
47- device node : /dev/dasdd
48- busid : 0.0.1518
49- type : ECKD
50- device type : hex 3390 dec 13200
51-
52- --- DASD geometry --------------------------------------------------------
53- number of cylinders : hex 2721 dec 10017
54- tracks per cylinder : hex f dec 15
55- blocks per track : hex c dec 12
56- blocksize : hex 1000 dec 4096
57-
58- --- extended DASD information --------------------------------------------
59- real device number : hex 0 dec 0
60- subchannel identifier : hex 178 dec 376
61- CU type (SenseID) : hex 3990 dec 14736
62- CU model (SenseID) : hex e9 dec 233
63- device type (SenseID) : hex 3390 dec 13200
64- device model (SenseID) : hex c dec 12
65- open count : hex 1 dec 1
66- req_queue_len : hex 0 dec 0
67- chanq_len : hex 0 dec 0
68- status : hex 5 dec 5
69- label_block : hex 2 dec 2
70- FBA_layout : hex 0 dec 0
71- characteristics_size : hex 40 dec 64
72- confdata_size : hex 100 dec 256
73- format : hex 2 dec 2 CDL formatted
74- features : hex 0 dec 0 default
75-
76- characteristics : 3990e933 900c5e0c 39f72032 2721000f
77- e000e5a2 05940222 13090674 00000000
78- 00000000 00000000 32321502 dfee0001
79- 0677080f 007f4800 1f3c0000 00002721
80-
81- configuration_data : dc010100 f0f0f2f1 f0f7f9f0 f0c9c2d4
82- f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10818
83- d4020000 f0f0f2f1 f0f7f9f6 f1c9c2d4
84- f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10800
85- d0000000 f0f0f2f1 f0f7f9f6 f1c9c2d4
86- f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f00800
87- f0000001 f0f0f2f1 f0f7f9f0 f0c9c2d4
88- f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10800
89- 00000000 00000000 00000000 00000000
90- 00000000 00000000 00000000 00000000
91- 00000000 00000000 00000000 00000000
92- 00000000 00000000 00000000 00000000
93- 00000000 00000000 00000000 00000000
94- 00000000 00000000 00000000 00000000
95- 81000003 2d001e00 15000247 000c0016
96- 000cc018 935e41ee 00030000 0000a000
97-
98- Output:
99-
100- view = {
101- 'extended': {
102- 'chanq_len': Dasdvalue(hex='0x0', dec=0, txt=None),
103- 'characteristics': ['3990e933', ...], # shortened for brevity
104- 'characteristics_size': Dasdvalue(hex='0x40', dec=64, txt=None),
105- 'confdata_size': Dasdvalue(hex='0x100', dec=256, txt=None),
106- 'configuration_data': ['dc010100', ...], # shortened for brevity
107- 'cu_model_senseid': Dasdvalue(hex='0xe9', dec=233, txt=None),
108- 'cu_type__senseid': Dasdvalue(hex='0x3990', dec=14736, txt=None),
109- 'device_model_senseid': Dasdvalue(hex='0xc', dec=12, txt=None),
110- 'device_type__senseid': Dasdvalue(hex='0x3390', dec=13200, txt=None),
111- 'fba_layout': Dasdvalue(hex='0x0', dec=0, txt=None),
112- 'features': Dasdvalue(hex='0x0', dec=0, txt='default'),
113- 'format': Dasdvalue(hex='0x2', dec=2, txt='cdl'),
114- 'label_block': Dasdvalue(hex='0x2', dec=2, txt=None),
115- 'open_count': Dasdvalue(hex='0x1', dec=1, txt=None),
116- 'real_device_number': Dasdvalue(hex='0x0', dec=0, txt=None),
117- 'req_queue_len': Dasdvalue(hex='0x0', dec=0, txt=None),
118- 'status': Dasdvalue(hex='0x5', dec=5, txt=None),
119- 'subchannel_identifier': Dasdvalue(hex='0x178', dec=376, txt=None)},
120- 'general': {
121- 'busid': ['0.0.1518'],
122- 'device_node': ['/dev/dasdd'],
123- 'device_type': Dasdvalue(hex='0x3390', dec=13200, txt=None),
124- 'type': ['ECKD']},
125- 'geometry': {
126- 'blocks_per_track': Dasdvalue(hex='0xc', dec=12, txt=None),
127- 'blocksize': Dasdvalue(hex='0x1000', dec=4096, txt=None),
128- 'number_of_cylinders': Dasdvalue(hex='0x2721', dec=10017, txt=None),
129- 'tracks_per_cylinder': Dasdvalue(hex='0xf', dec=15, txt=None)}
130- }
131- """
132+ return _dasd_format(out)
133
134- info_key_map = {
135- 'CDL formatted': 'cdl',
136- 'LDL formatted': 'ldl',
137- 'NOT formatted': 'not-formatted',
138- }
139
140- def _mkdasdvalue(hex_str, dec_str, comment=None):
141- v_hex = hex_str.replace('hex ', '0x')
142- v_dec = int(dec_str.replace('dec ', ''))
143- v_str = None
144- if comment is not None:
145- v_str = info_key_map.get(comment, comment)
146-
147- return Dasdvalue(v_hex, v_dec, v_str)
148-
149- def _map_strip(value):
150- v_type = type(value)
151- return v_type(map(lambda x: x.strip(), value))
152-
153- def _parse_output(output):
154- parsed = {}
155- key = prev_key = value = None
156- for line in output:
157- if not line:
158- continue
159- if ':' in line:
160- key, value = map(lambda x: x.strip(),
161- line.lstrip().split(':'))
162- # normalize lvalue
163- key = key.replace(' ', '_')
164- key = key.replace(' ', '_')
165- key = key.replace('(', '').replace(')', '')
166- key = key.lower()
167- prev_key = key
168- if value and '\t' in value:
169- value = _map_strip(value.split('\t'))
170- # [hex X, dec Y]
171- # [hex X, dec Y, string]
172- value = _mkdasdvalue(*value)
173- elif value and ' ' in value:
174- # characteristics : XXXXXXX XXXXXXX XXXXXXXX XXXXXXX
175- # YYYYYYY YYYYYYY YYYYYYYY YYYYYYY
176- # convert to list of strings.
177- value = value.lstrip().split()
178- else:
179- value = None
180- else:
181- key = prev_key
182- # no colon line, parse value from line
183- # YYYYYYY YYYYYYY YYYYYYYY YYYYYYY
184- value = line.lstrip().split()
185-
186- # extend lists for existing keys
187- if key in parsed and type(value) == list:
188- parsed[key].extend(value)
189- else:
190- parsed.update({key: value})
191-
192- return parsed
193-
194- if not view_output or not isinstance(view_output, util.string_types):
195- raise ValueError(
196- 'Invalid value for input to parse: ' + str(view_output))
197+DASD_FORMAT = r"^format\s+:.+\s+(?P<value>\w+\s\w+)$"
198+
199+
200+def find_val(regex, content):
201+ m = re.search(regex, content, re.MULTILINE)
202+ if m is not None:
203+ return m.group("value")
204
205- # XXX: dasdview --extended has 52 lines for dasd devices
206- if len(view_output.splitlines()) < 52:
207- raise ValueError(
208- 'dasdview output has fewer than 52 lines, cannot parse')
209
210- lines = view_output.splitlines()
211- gen_start, gen_end = (2, 6)
212- geo_start, geo_end = (8, 12)
213- ext_start, ext_end = (14, len(lines))
214+def _dasd_format(dasdview_output):
215+ """ Read and return specified device "disk_layout" value.
216
217- general_output = lines[gen_start:gen_end]
218- geometry_output = lines[geo_start:geo_end]
219- extended_output = lines[ext_start:ext_end]
220+ :returns: string: One of ['cdl', 'ldl', 'not-formatted'].
221+ :raises: ValueError if dasdview result missing 'format' section.
222
223- view = {
224- 'general': _parse_output(general_output),
225- 'geometry': _parse_output(geometry_output),
226- 'extended': _parse_output(extended_output),
227+ """
228+ if not dasdview_output:
229+ return
230+
231+ mapping = {
232+ 'cdl formatted': 'cdl',
233+ 'ldl formatted': 'ldl',
234+ 'not formatted': 'not-formatted',
235 }
236- return view
237+ diskfmt = find_val(DASD_FORMAT, dasdview_output)
238+ if diskfmt is not None:
239+ return mapping.get(diskfmt.lower())
240
241
242 def _valid_device_id(device_id):
243@@ -371,38 +217,9 @@ class CcwDevice(object):
244
245 class DasdDevice(CcwDevice):
246
247- def __init__(self, device_id):
248- super(DasdDevice, self).__init__(device_id)
249- self._kname = None
250-
251- @property
252- def kname(self):
253- if not self._kname:
254- self._kname = self._get_kname()
255- return self._kname
256-
257- def _get_kname(self):
258- """ Return the kernel name of the dasd device. """
259- if not self.is_online():
260- raise RuntimeError('Cannot determine dasd kname for offline '
261- 'device: %s' % self.device_id)
262-
263- blockdir = self.ccw_device_attr_path('block')
264- if not os.path.isdir(blockdir):
265- raise RuntimeError('Unexpectedly not a directory: %s' % blockdir)
266-
267- try:
268- knames = os.listdir(blockdir)
269- [dasd_kname] = knames
270- except ValueError:
271- raise RuntimeError('Unexpected os.listdir result at sysfs path '
272- '%s: "%s"' % (blockdir, knames))
273-
274- return dasd_kname
275-
276 @property
277 def devname(self):
278- return '/dev/%s' % self.kname
279+ return '/dev/disk/by-path/ccw-%s' % self.device_id
280
281 def partition(self, partnumber, partsize, strict=True):
282 """ Add a partition to this DasdDevice specifying partnumber and size.
283@@ -461,42 +278,36 @@ class DasdDevice(CcwDevice):
284 """ Returns a boolean indicating if the specified device_id is not yet
285 formatted.
286
287- :param device_id: string of device ccw bus_id.
288 :returns: boolean: True if the device is not formatted.
289 """
290 return self.ccw_device_attr('status') == "unformatted"
291
292- def is_online(self):
293- """ Returns a boolean indicating if specified device is online.
294-
295- :param device_id: string of device ccw bus_id.
296- :returns: boolean: True if device is online.
297- """
298- return self.ccw_device_attr('online') == "1"
299-
300 def blocksize(self):
301 """ Read and return device_id's 'blocksize' value.
302
303 :param: device_id: string of device ccw bus_id.
304 :returns: string: the device's current blocksize.
305 """
306- blkattr = 'block/%s/queue/hw_sector_size' % self.kname
307- return self.ccw_device_attr(blkattr)
308+ blkattr = 'block/*/queue/hw_sector_size'
309+ # In practice there will only be one entry in the directory
310+ # /sys/bus/ccw/devices/{device_id}/block/, but in case
311+ # something strange happens and there are more, this assumes
312+ # all block devices connected to the dasd have the same block
313+ # size...
314+ path = glob.glob(self.ccw_device_attr_path(blkattr))[0]
315+ return util.load_file(path)
316
317 def disk_layout(self):
318 """ Read and return specified device "disk_layout" value.
319
320 :returns: string: One of ['cdl', 'ldl', 'not-formatted'].
321 :raises: ValueError if dasdview result missing 'format' section.
322-
323 """
324- view = dasdview(self.devname)
325- disk_format = view.get('extended', {}).get('format')
326- if not disk_format:
327+ format = dasd_format(self.devname)
328+ if not format:
329 raise ValueError(
330- 'dasdview on %s missing "format" section' % self.devname)
331-
332- return disk_format.txt
333+ 'could not determine format of %s' % self.devname)
334+ return format
335
336 def label(self):
337 """Read and return specified device label (VOLSER) value.
338@@ -546,7 +357,7 @@ class DasdDevice(CcwDevice):
339
340 @logged_time("DASD.FORMAT")
341 def format(self, blksize=4096, layout='cdl', force=False, set_label=None,
342- keep_label=False, no_label=False, mode='quick', strict=True):
343+ keep_label=False, no_label=False, mode='quick'):
344 """ Format DasdDevice with supplied parameters.
345
346 :param blksize: integer value to configure disk block size in bytes.
347@@ -572,7 +383,7 @@ class DasdDevice(CcwDevice):
348 :param strict: boolean which enforces that dasd device exists before
349 issuing format command, defaults to True.
350
351- :raises: RuntimeError if strict==True and devname does not exist.
352+ :raises: RuntimeError if devname does not exist.
353 :raises: ValueError on invalid blocksize, disk_layout and mode.
354 :raises: ProcessExecutionError on errors running 'dasdfmt' command.
355
356@@ -580,7 +391,7 @@ class DasdDevice(CcwDevice):
357 dasdformat -y --blocksize=4096 --disk_layout=cdl \
358 --mode=quick /dev/dasda
359 """
360- if strict and not os.path.exists(self.devname):
361+ if not os.path.exists(self.devname):
362 raise RuntimeError("devname '%s' does not exist" % self.devname)
363
364 if no_label:
365diff --git a/tests/unittests/test_block_dasd.py b/tests/unittests/test_block_dasd.py
366index dfd62d3..fffa4a1 100644
367--- a/tests/unittests/test_block_dasd.py
368+++ b/tests/unittests/test_block_dasd.py
369@@ -85,80 +85,18 @@ class TestDasdValidDeviceId(CiTestCase):
370 dasd.DasdDevice(invalid_dev)
371
372
373-class TestDasdDeviceIdToKname(CiTestCase):
374-
375- def setUp(self):
376- super(TestDasdDeviceIdToKname, self).setUp()
377- self.add_patch('curtin.block.dasd.os.path.isdir', 'm_isdir')
378- self.add_patch('curtin.block.dasd.os.listdir', 'm_listdir')
379- self.add_patch('curtin.block.dasd.DasdDevice.is_online', 'm_online')
380-
381- self.dasd = dasd.DasdDevice(random_device_id())
382- self.m_online.return_value = True
383- self.m_isdir.return_value = True
384- self.m_listdir.return_value = [self.random_string()]
385-
386- def test_device_id_to_kname_returns_kname(self):
387- """returns a dasd kname if device_id is valid and online """
388- result = self.dasd.kname
389- self.assertIsNotNone(result)
390- self.assertEqual(result, self.m_listdir.return_value[0])
391-
392- def test_devid_to_kname_raises_runtimeerror_no_online(self):
393- """device_id_to_kname raises RuntimeError on offline devices."""
394- self.m_online.return_value = False
395- result = None
396- with self.assertRaises(RuntimeError):
397- result = self.dasd.kname
398- self.assertEqual(result, None)
399- self.assertEqual(1, self.m_online.call_count)
400- self.assertEqual(0, self.m_isdir.call_count)
401- self.assertEqual(0, self.m_listdir.call_count)
402-
403- def test_devid_to_kname_raises_runtimeerror_no_blockpath(self):
404- """device_id_to_kname raises RuntimeError on invalid sysfs path."""
405- self.m_isdir.return_value = False
406- result = None
407- with self.assertRaises(RuntimeError):
408- result = self.dasd.kname
409- self.assertEqual(result, None)
410- self.assertEqual(1, self.m_online.call_count)
411- self.assertEqual(1, self.m_isdir.call_count)
412- self.m_isdir.assert_called_with(
413- '/sys/bus/ccw/devices/%s/block' % self.dasd.device_id)
414- self.assertEqual(0, self.m_listdir.call_count)
415-
416- def test_devid_to_kname_raises_runtimeerror_empty_blockdir(self):
417- """device_id_to_kname raises RuntimeError on empty blockdir."""
418- self.m_listdir.return_value = []
419- result = None
420- with self.assertRaises(RuntimeError):
421- result = self.dasd.kname
422- self.assertEqual(result, None)
423- self.assertEqual(1, self.m_online.call_count)
424- self.assertEqual(1, self.m_isdir.call_count)
425- self.m_isdir.assert_called_with(
426- '/sys/bus/ccw/devices/%s/block' % self.dasd.device_id)
427- self.m_listdir.assert_called_with(
428- '/sys/bus/ccw/devices/%s/block' % self.dasd.device_id)
429- self.assertEqual(1, self.m_listdir.call_count)
430-
431-
432 class TestDasdCcwDeviceAttr(CiTestCase):
433
434 def setUp(self):
435 super(TestDasdCcwDeviceAttr, self).setUp()
436 self.add_patch('curtin.block.dasd.os.path.isfile', 'm_isfile')
437 self.add_patch('curtin.block.dasd.util.load_file', 'm_loadfile')
438- dpath = 'curtin.block.dasd.DasdDevice'
439- self.add_patch(dpath + '.kname', 'm_kname')
440+ self.add_patch('curtin.block.dasd.glob.glob', 'm_glob')
441
442 # defaults
443 self.m_isfile.return_value = True
444 self.m_loadfile.return_value = self.random_string()
445 self.dasd = dasd.DasdDevice(random_device_id())
446- self.kname = self.random_string()
447- self.m_kname.return_value = self.kname
448
449 def _test_ccw_attr(self, my_attr=None, attr_val_in=None, attr_val=None):
450 if not my_attr:
451@@ -200,60 +138,37 @@ class TestDasdCcwDeviceAttr(CiTestCase):
452 self.m_loadfile.return_value = self.random_string()
453 self.assertFalse(self.dasd.is_not_formatted())
454
455- def test_is_online_returns_false_if_not_online(self):
456- self.m_loadfile.return_value = self.random_string()
457- self.assertFalse(self.dasd.is_online())
458-
459 def test_blocksize(self):
460 blocksize_val = '%d' % random.choice([512, 1024, 2048, 4096])
461+ path = self.random_string()
462+ self.m_glob.return_value = [path]
463 self.m_loadfile.return_value = blocksize_val
464 self.assertEqual(blocksize_val, self.dasd.blocksize())
465+ self.m_loadfile.assert_called_once_with(path)
466
467
468 class TestDiskLayout(CiTestCase):
469
470- layouts = {
471- 'not-formatted': dasd.Dasdvalue(0x0, 0, 'not-formatted'),
472- 'ldl': dasd.Dasdvalue(0x1, 1, 'ldl'),
473- 'cdl': dasd.Dasdvalue(0x2, 2, 'cdl'),
474- }
475-
476 def setUp(self):
477 super(TestDiskLayout, self).setUp()
478- self.add_patch('curtin.block.dasd.os.path.exists', 'm_exists')
479- self.add_patch('curtin.block.dasd.dasdview', 'm_dasdview')
480+ self.add_patch('curtin.block.dasd.dasd_format', 'm_dasd_format')
481 dpath = 'curtin.block.dasd.DasdDevice'
482 self.add_patch(dpath + '.devname', 'm_devname')
483
484- # defaults
485- self.m_exists.return_value = True
486- self.m_dasdview.return_value = self._mkview()
487 self.dasd = dasd.DasdDevice(random_device_id())
488 self.devname = self.random_string()
489 self.m_devname.return_value = self.devname
490
491- @classmethod
492- def _mkview(cls, layout=None):
493- if not layout:
494- layout = random.choice(list(cls.layouts.values()))
495- return {'extended': {'format': layout}}
496-
497- # XXX: Parameterize me
498- def test_disk_layout_returns_dasd_extended_format_value(self):
499+ def test_disk_layout_returns_dasd_format_result(self):
500 """disk_layout returns dasd disk_layout format as string"""
501- for my_layout in self.layouts.values():
502- self.m_dasdview.return_value = self._mkview(layout=my_layout)
503- self.assertEqual(my_layout.txt, self.dasd.disk_layout())
504+ expected = self.m_dasd_format.return_value = self.random_string()
505+ self.assertEqual(expected, self.dasd.disk_layout())
506
507- # XXX: Parameterize me
508- def test_disk_layout_raises_valueerror_on_missing_format_section(self):
509+ def test_disk_layout_raises_valueerror_on_missing_format(self):
510 """disk_layout raises ValueError if view missing 'format' section."""
511- for my_layout in self.layouts.values():
512- view = self._mkview(layout=my_layout)
513- view['extended']['format'] = None
514- self.m_dasdview.return_value = view
515- with self.assertRaises(ValueError):
516- self.dasd.disk_layout()
517+ self.m_dasd_format.return_value = None
518+ with self.assertRaises(ValueError):
519+ self.dasd.disk_layout()
520
521
522 class TestLabel(CiTestCase):
523@@ -287,8 +202,6 @@ class TestLabel(CiTestCase):
524
525 class TestNeedsFormatting(CiTestCase):
526
527- blocksizes = [512, 1024, 2048, 4096]
528-
529 def setUp(self):
530 super(TestNeedsFormatting, self).setUp()
531 dpath = 'curtin.block.dasd.DasdDevice'
532@@ -298,8 +211,10 @@ class TestNeedsFormatting(CiTestCase):
533 self.add_patch(dpath + '.label', 'm_label')
534
535 self.m_not_fmt.return_value = False
536- self.m_blocksize.return_value = 4096
537- self.m_disk_layout.return_value = TestDiskLayout.layouts['cdl'].txt
538+ self.blocksize = 4096
539+ self.m_blocksize.return_value = self.blocksize
540+ self.disk_layout = self.random_string()
541+ self.m_disk_layout.return_value = self.disk_layout
542 self.label = self.random_string()
543 self.m_label.return_value = self.label
544
545@@ -308,26 +223,34 @@ class TestNeedsFormatting(CiTestCase):
546 def test_needs_formatting_label_mismatch(self):
547 my_label = self.random_string()
548 self.assertNotEqual(self.label, my_label)
549- self.assertTrue(self.dasd.needs_formatting(4096, 'cdl', my_label))
550+ self.assertTrue(
551+ self.dasd.needs_formatting(
552+ self.blocksize, self.disk_layout, my_label))
553
554 def test_needs_formatting_layout_mismatch(self):
555- my_layout = TestDiskLayout.layouts['ldl'].txt
556+ my_layout = self.random_string()
557+ self.assertNotEqual(self.disk_layout, my_layout)
558 self.assertTrue(
559- self.dasd.needs_formatting(4096, my_layout, self.label))
560+ self.dasd.needs_formatting(
561+ self.blocksize, my_layout, self.label))
562
563 def test_needs_formatting_blocksize_mismatch(self):
564- my_blocksize = random.choice(self.blocksizes[0:3])
565+ my_blocksize = random.randrange(self.blocksize)
566+ self.assertNotEqual(self.blocksize, my_blocksize)
567 self.assertTrue(
568- self.dasd.needs_formatting(my_blocksize, 'cdl', self.label))
569+ self.dasd.needs_formatting(
570+ my_blocksize, self.disk_layout, self.label))
571
572 def test_needs_formatting_unformatted_disk(self):
573 self.m_not_fmt.return_value = True
574 self.assertTrue(
575- self.dasd.needs_formatting(4096, 'cdl', self.label))
576+ self.dasd.needs_formatting(
577+ self.blocksize, self.disk_layout, self.label))
578
579 def test_needs_formatting_ignores_label_mismatch(self):
580 self.assertFalse(
581- self.dasd.needs_formatting(4096, 'cdl', None))
582+ self.dasd.needs_formatting(
583+ self.blocksize, self.disk_layout, None))
584
585
586 class TestFormat(CiTestCase):
587@@ -346,10 +269,6 @@ class TestFormat(CiTestCase):
588 self.devname = self.random_string()
589 self.m_devname.return_value = self.devname
590
591- def test_format_devname_ignores_path_missing_strict_false(self):
592- self.m_exists.return_value = False
593- self.dasd.format(strict=False)
594-
595 def test_format_defaults_match_docstring(self):
596 self.dasd.format()
597 self.m_subp.assert_called_with(
598@@ -454,9 +373,39 @@ class TestDasdInfo(CiTestCase):
599 dasd.dasdinfo(device_id)
600
601
602-class TestDasdView(CiTestCase):
603+class TestDasdFormat(CiTestCase):
604+
605+ def setUp(self):
606+ super(TestDasdFormat, self).setUp()
607+ self.add_patch('curtin.block.dasd.util.subp', 'm_subp')
608+ self.add_patch('curtin.block.dasd.os.path.exists', 'm_exists')
609+ self.add_patch('curtin.block.dasd._dasd_format', 'm_dasd_format')
610+
611+ # defaults
612+ self.m_exists.return_value = True
613+ self.m_subp.return_value = ('', '')
614+
615+ def test_dasd_format_raise_error_on_invalid_device(self):
616+ """dasdview raises error on invalid device path."""
617+ devname = self.random_string()
618+ self.m_exists.return_value = False
619+ with self.assertRaises(ValueError):
620+ dasd.dasd_format(devname)
621+
622+ def test_dasd_foramt_calls__dasd_format(self):
623+ """dasdview calls parser on output from dasdview command."""
624+ devname = self.random_string()
625+ view = self.random_string()
626+ self.m_subp.return_value = (view, self.random_string())
627+ dasd.dasd_format(devname)
628+ self.m_subp.assert_called_with(
629+ ['dasdview', '--extended', devname], capture=True)
630+ self.m_dasd_format.assert_called_with(view)
631+
632+
633+class Test_DasdFormat(CiTestCase):
634
635- view = textwrap.dedent("""\
636+ view_output_template = textwrap.dedent("""\
637
638 --- general DASD information ----------------------------------------------
639 device node : /dev/dasdd
640@@ -485,7 +434,7 @@ class TestDasdView(CiTestCase):
641 FBA_layout : hex 0 dec 0
642 characteristics_size : hex 40 dec 64
643 confdata_size : hex 100 dec 256
644- format : hex 2 dec 2 CDL formatted
645+ format : hex 2 dec 2 {format}
646 features : hex 0 dec 0 default
647
648 characteristics : 3990e933 900c5e0c 39f72032 2721000f
649@@ -510,59 +459,15 @@ class TestDasdView(CiTestCase):
650 81000003 2d001e00 15000247 000c0016
651 000cc018 935e41ee 00030000 0000a000""")
652
653- view_nondasd = textwrap.dedent("""\
654- Error: dasdview: Could not retrieve disk information!
655-
656- """)
657-
658- def setUp(self):
659- super(TestDasdView, self).setUp()
660- self.add_patch('curtin.block.dasd.util.subp', 'm_subp')
661- self.add_patch('curtin.block.dasd.os.path.exists', 'm_exists')
662- self.add_patch('curtin.block.dasd._parse_dasdview', 'm_parseview')
663-
664- # defaults
665- self.m_exists.return_value = True
666- self.m_subp.return_value = ('', '')
667-
668- def test_dasdview_raise_error_on_invalid_device(self):
669- """dasdview raises error on invalid device path."""
670- devname = self.random_string()
671- self.m_exists.return_value = False
672- with self.assertRaises(ValueError):
673- dasd.dasdview(devname)
674-
675- def test_dasdview_calls_parse_dasdview(self):
676- """dasdview calls parser on output from dasdview command."""
677- devname = self.random_string()
678- self.m_subp.return_value = (self.view, self.random_string())
679- dasd.dasdview(devname)
680- self.m_subp.assert_called_with(
681- ['dasdview', '--extended', devname], capture=True)
682- self.m_parseview.assert_called_with(self.view)
683-
684-
685-class TestParseDasdView(CiTestCase):
686-
687- def test_parse_dasdview_no_input(self):
688- """_parse_dasdview raises ValueError on invalid status input."""
689- for view_output in [None, 123, {}, (), []]:
690- with self.assertRaises(ValueError):
691- dasd._parse_dasdview(view_output)
692-
693- def test_parse_dasdview_invalid_strings_short(self):
694- """_parse_dasdview raises ValueError on invalid long input."""
695- with self.assertRaises(ValueError):
696- dasd._parse_dasdview("\n".join([self.random_string()] * 20))
697-
698- def test_parse_dasdview_returns_dictionary(self):
699+ def test__dasd_format_returns_format(self):
700 """_parse_dasdview returns dict w/ required keys parsing valid view."""
701- result = dasd._parse_dasdview(TestDasdView.view)
702- self.assertEqual(dict, type(result))
703- self.assertNotEqual({}, result)
704- self.assertEqual(3, len(result.keys()))
705- for key in result.keys():
706- self.assertEqual(dict, type(result[key]))
707- self.assertNotEqual(0, len(list(result[key].keys())))
708+ for (format, expected) in [
709+ ('CDL formatted', 'cdl'),
710+ ('LDL formatted', 'ldl'),
711+ ('not formatted', 'not-formatted'),
712+ ]:
713+ result = dasd._dasd_format(
714+ self.view_output_template.format(format=format))
715+ self.assertEqual(result, expected)
716
717 # vi: ts=4 expandtab syntax=python

Subscribers

People subscribed via source and target branches