diff -Nru probert-0.0.17build2/debian/changelog probert-0.0.18/debian/changelog --- probert-0.0.17build2/debian/changelog 2020-02-18 08:38:26.000000000 +0000 +++ probert-0.0.18/debian/changelog 2020-02-25 21:14:40.000000000 +0000 @@ -1,3 +1,16 @@ +probert (0.0.18) focal; urgency=medium + + [Ryan Harper] + * d/control + - Add s390-tools dep on s390x architecture + * Add probing support for DASD devices on s390x + * Fix storage size calculations from sysfs (Issue #74) + + [Michael Hudson-Doyle] + * Fix segfault when netdev is removed + + -- Ryan Harper Tue, 25 Feb 2020 15:14:40 -0600 + probert (0.0.17build2) focal; urgency=medium * No-change rebuild to drop python3.7. diff -Nru probert-0.0.17build2/debian/control probert-0.0.18/debian/control --- probert-0.0.17build2/debian/control 2019-09-02 12:23:38.000000000 +0000 +++ probert-0.0.18/debian/control 2020-02-25 21:14:40.000000000 +0000 @@ -14,8 +14,8 @@ python3-coverage, python3-flake8, python3-jsonschema, - python3-nose, python3-mock, + python3-nose, python3-pyudev, python3-setuptools, python3-testtools @@ -26,7 +26,7 @@ Package: probert Architecture: all -Depends: ${misc:Depends}, probert-storage, probert-network +Depends: probert-network, probert-storage, ${misc:Depends} Description: Hardware probing tool - metapackage This package provides a tool for probing host hardware information and emitting a JSON report. @@ -35,9 +35,7 @@ Package: probert-common Architecture: all -Depends: ${misc:Depends}, - ${python3:Depends}, - ${shlibs:Depends} +Depends: ${misc:Depends}, ${python3:Depends}, ${shlibs:Depends} Breaks: probert (<< 0.0.16) Replaces: probert (<< 0.0.16) Description: Hardware probing tool - common @@ -47,12 +45,13 @@ This package provides the common code for probing. Package: probert-storage -Architecture: all -Depends: probert-common (= ${source:Version}), - bcache-tools, +Architecture: any +Depends: bcache-tools, lvm2, mdadm, multipath-tools, + probert-common (= ${source:Version}), + s390-tools [s390x], zfsutils-linux, ${misc:Depends}, ${python3:Depends}, diff -Nru probert-0.0.17build2/debian/probert-common.install probert-0.0.18/debian/probert-common.install --- probert-0.0.17build2/debian/probert-common.install 2019-09-02 11:53:20.000000000 +0000 +++ probert-0.0.18/debian/probert-common.install 2020-02-25 21:14:40.000000000 +0000 @@ -1,18 +1,21 @@ usr/bin/probert usr/share/probert/bin -usr/lib/python3.*/dist-packages/probert-*.egg-info/requires.txt -usr/lib/python3.*/dist-packages/probert-*.egg-info/dependency_links.txt usr/lib/python3.*/dist-packages/probert-*.egg-info/PKG-INFO +usr/lib/python3.*/dist-packages/probert-*.egg-info/dependency_links.txt +usr/lib/python3.*/dist-packages/probert-*.egg-info/requires.txt usr/lib/python3.*/dist-packages/probert-*.egg-info/top_level.txt +usr/lib/python3.*/dist-packages/probert/__init__.py usr/lib/python3.*/dist-packages/probert/log.py -usr/lib/python3.*/dist-packages/probert/utils.py usr/lib/python3.*/dist-packages/probert/prober.py -usr/lib/python3.*/dist-packages/probert/__init__.py -usr/lib/python3.*/dist-packages/probert/tests/test_utils.py +usr/lib/python3.*/dist-packages/probert/tests/__init__.py +usr/lib/python3.*/dist-packages/probert/tests/data/dasdd.view +usr/lib/python3.*/dist-packages/probert/tests/data/dasde.view usr/lib/python3.*/dist-packages/probert/tests/data/fake_probe_all.json usr/lib/python3.*/dist-packages/probert/tests/fakes.py -usr/lib/python3.*/dist-packages/probert/tests/test_storage.py -usr/lib/python3.*/dist-packages/probert/tests/test_lvm.py -usr/lib/python3.*/dist-packages/probert/tests/__init__.py usr/lib/python3.*/dist-packages/probert/tests/helpers.py +usr/lib/python3.*/dist-packages/probert/tests/test_dasd.py +usr/lib/python3.*/dist-packages/probert/tests/test_lvm.py usr/lib/python3.*/dist-packages/probert/tests/test_multipath.py usr/lib/python3.*/dist-packages/probert/tests/test_prober.py +usr/lib/python3.*/dist-packages/probert/tests/test_storage.py +usr/lib/python3.*/dist-packages/probert/tests/test_utils.py +usr/lib/python3.*/dist-packages/probert/utils.py diff -Nru probert-0.0.17build2/debian/probert-network.install probert-0.0.18/debian/probert-network.install --- probert-0.0.17build2/debian/probert-network.install 2019-09-02 11:53:20.000000000 +0000 +++ probert-0.0.18/debian/probert-network.install 2020-02-25 21:14:40.000000000 +0000 @@ -1,5 +1,5 @@ -usr/lib/python3.*/dist-packages/probert/_nl80211module.c -usr/lib/python3.*/dist-packages/probert/network.py -usr/lib/python3.*/dist-packages/probert/_rtnetlinkmodule.c usr/lib/python3.*/dist-packages/probert/_nl80211.*.so +usr/lib/python3.*/dist-packages/probert/_nl80211module.c usr/lib/python3.*/dist-packages/probert/_rtnetlink.*.so +usr/lib/python3.*/dist-packages/probert/_rtnetlinkmodule.c +usr/lib/python3.*/dist-packages/probert/network.py diff -Nru probert-0.0.17build2/debian/probert-storage.install probert-0.0.18/debian/probert-storage.install --- probert-0.0.17build2/debian/probert-storage.install 2019-09-02 11:53:20.000000000 +0000 +++ probert-0.0.18/debian/probert-storage.install 2020-02-25 21:14:40.000000000 +0000 @@ -1,9 +1,10 @@ -usr/lib/python3.*/dist-packages/probert/raid.py -usr/lib/python3.*/dist-packages/probert/multipath.py -usr/lib/python3.*/dist-packages/probert/zfs.py usr/lib/python3.*/dist-packages/probert/bcache.py -usr/lib/python3.*/dist-packages/probert/lvm.py +usr/lib/python3.*/dist-packages/probert/dasd.py usr/lib/python3.*/dist-packages/probert/dmcrypt.py -usr/lib/python3.*/dist-packages/probert/storage.py -usr/lib/python3.*/dist-packages/probert/mount.py usr/lib/python3.*/dist-packages/probert/filesystem.py +usr/lib/python3.*/dist-packages/probert/lvm.py +usr/lib/python3.*/dist-packages/probert/mount.py +usr/lib/python3.*/dist-packages/probert/multipath.py +usr/lib/python3.*/dist-packages/probert/raid.py +usr/lib/python3.*/dist-packages/probert/storage.py +usr/lib/python3.*/dist-packages/probert/zfs.py diff -Nru probert-0.0.17build2/debian/watch probert-0.0.18/debian/watch --- probert-0.0.17build2/debian/watch 1970-01-01 00:00:00.000000000 +0000 +++ probert-0.0.18/debian/watch 2020-02-25 21:14:40.000000000 +0000 @@ -0,0 +1,2 @@ +version=4 +https://github.com/CanonicalLtd/probert/releases .*/v(.*).tar.gz diff -Nru probert-0.0.17build2/probert/dasd.py probert-0.0.18/probert/dasd.py --- probert-0.0.17build2/probert/dasd.py 1970-01-01 00:00:00.000000000 +0000 +++ probert-0.0.18/probert/dasd.py 2020-02-25 21:14:40.000000000 +0000 @@ -0,0 +1,143 @@ +# Copyright 2020 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import logging +import os +import platform +import pyudev +import re +import subprocess + +log = logging.getLogger('probert.dasd') + +DASD_FORMAT = r"^format\s+:.+\s+(?P\w+\s\w+)$" +DASD_BLKSIZE = r"^blocksize\s+:\shex\s\w+\s+dec\s(?P\d+)$" + + +def _search(regex, content, groupkey): + m = re.search(regex, content, re.MULTILINE) + if m: + return m.group(groupkey) + + +def blocksize(dasdview_output): + """ Read and return device_id's 'blocksize' value. + + :param: device_id: string of device ccw bus_id. + :returns: int: the device's current blocksize. + """ + if not dasdview_output: + return + + blksize = _search(DASD_BLKSIZE, dasdview_output, 'blksize') + if blksize: + return int(blksize) + + +def disk_format(dasdview_output): + """ Read and return specified device "disk_layout" value. + + :returns: string: One of ['cdl', 'ldl', 'not-formatted']. + :raises: ValueError if dasdview result missing 'format' section. + + """ + if not dasdview_output: + return + + mapping = { + 'cdl formatted': 'cdl', + 'ldl formatted': 'ldl', + 'not formatted': 'not-formatted', + } + diskfmt = _search(DASD_FORMAT, dasdview_output, 'format') + if diskfmt: + return mapping.get(diskfmt.lower()) + + +def dasdview(devname): + ''' Run dasdview on devname and return the output. + + dasdview --extended has 3 sections + general (2:6), geometry (8:12), extended (14:) + + ''' + if not os.path.exists(devname): + raise ValueError("Invalid dasd device name: '%s'" % devname) + + cmd = ['dasdview', '--extended', devname] + try: + result = subprocess.run(cmd, stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) + except (subprocess.CalledProcessError, FileNotFoundError): + log.error('Failed to run cmd: %s', cmd) + return None + + return result.stdout.decode('utf-8') + + +def get_dasd_info(device): + """ from a udev blockdev device entry, return all required dasd info + + """ + name = device.get('DEVNAME') + device_id = device.get('ID_PATH', '').replace('ccw-', '') + dasdview_output = dasdview(name) + diskfmt = disk_format(dasdview_output) + blksize = blocksize(dasdview_output) + if not all([name, device_id, diskfmt, blksize]): + vals = ("name=%s device_id=%s format=%s blksize=%s" % ( + name, device_id, diskfmt, blksize)) + log.debug('Failed to probe some DASD values: %s', vals) + return None + + return {'name': name, 'device_id': device_id, + 'disk_layout': diskfmt, 'blocksize': blksize} + + +def probe(context=None): + """Examine all dasd devices present and extract configuration attributes + + This data is useful for determining if the dasd device has been + formatted, if so what the block size, the partition layout used + and the s390x device_id used to uniquely identify the device. + """ + log.debug('Probing DASD devies') + machine = platform.machine() + if machine != "s390x": + log.debug('DASD devices only present on s390x, arch=%s', machine) + return {} + + dasds = {} + if not context: + context = pyudev.Context() + + for device in context.list_devices(subsystem='block'): + # dasd devices have MAJOR 94 + if device['MAJOR'] != "94": + continue + # ignore dasd partitions + if 'PARTN' in device: + continue + + try: + dasd_info = get_dasd_info(device) + except ValueError as e: + log.error('Error probing dasd device %s: %s', device['DEVNAME'], e) + dasd_info = None + + if dasd_info: + dasds[device['DEVNAME']] = dasd_info + + return dasds diff -Nru probert-0.0.17build2/probert/__init__.py probert-0.0.18/probert/__init__.py --- probert-0.0.17build2/probert/__init__.py 2019-09-02 11:53:20.000000000 +0000 +++ probert-0.0.18/probert/__init__.py 2020-02-25 21:14:40.000000000 +0000 @@ -15,4 +15,4 @@ """ Probert """ -__version__ = "0.0.16" +__version__ = "0.0.18" diff -Nru probert-0.0.17build2/probert/lvm.py probert-0.0.18/probert/lvm.py --- probert-0.0.17build2/probert/lvm.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/lvm.py 2020-02-25 21:14:40.000000000 +0000 @@ -19,7 +19,7 @@ import pyudev import subprocess -from probert.utils import read_sys_block_size +from probert.utils import read_sys_block_size_bytes log = logging.getLogger('probert.lvm') @@ -122,7 +122,8 @@ lv_id, {'fullname': lv_id, 'name': probe_data['DM_LV_NAME'], 'volgroup': probe_data['DM_VG_NAME'], - 'size': "%sB" % read_sys_block_size(probe_data['DEVNAME'])}) + 'size': "%sB" % read_sys_block_size_bytes( + probe_data['DEVNAME'])}) def extract_lvm_volgroup(vg_name, report_data): diff -Nru probert-0.0.17build2/probert/raid.py probert-0.0.18/probert/raid.py --- probert-0.0.17build2/probert/raid.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/raid.py 2020-02-25 21:14:40.000000000 +0000 @@ -17,7 +17,7 @@ import pyudev import subprocess -from probert.utils import (read_sys_block_size, +from probert.utils import (read_sys_block_size_bytes, udev_get_attributes) log = logging.getLogger('probert.raid') @@ -115,7 +115,7 @@ if 'MD_NAME' in device and device.get('DEVTYPE') == 'disk': devname = device['DEVNAME'] attrs = udev_get_attributes(device) - attrs['size'] = str(read_sys_block_size(devname)) + attrs['size'] = str(read_sys_block_size_bytes(devname)) devices, spares = get_mdadm_array_members(devname, device) cfg = dict(device) cfg.update({'raidlevel': device['MD_LEVEL'], diff -Nru probert-0.0.17build2/probert/_rtnetlinkmodule.c probert-0.0.18/probert/_rtnetlinkmodule.c --- probert-0.0.17build2/probert/_rtnetlinkmodule.c 2019-08-13 17:31:46.000000000 +0000 +++ probert-0.0.18/probert/_rtnetlinkmodule.c 2020-02-25 21:14:40.000000000 +0000 @@ -76,6 +76,10 @@ int is_vlan, ifindex; unsigned int flags; + if (act == NL_ACT_DEL) { + link = old_link; + } + is_vlan = rtnl_link_is_vlan(link); ifindex = rtnl_link_get_ifindex(link); flags = rtnl_link_get_flags(link); diff -Nru probert-0.0.17build2/probert/storage.py probert-0.0.18/probert/storage.py --- probert-0.0.17build2/probert/storage.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/storage.py 2020-02-25 21:14:40.000000000 +0000 @@ -18,8 +18,8 @@ import pyudev import subprocess -from probert.utils import udev_get_attributes, read_sys_block_size -from probert import (bcache, dmcrypt, filesystem, lvm, mount, multipath, +from probert.utils import udev_get_attributes, read_sys_block_size_bytes +from probert import (bcache, dasd, dmcrypt, filesystem, lvm, mount, multipath, raid, zfs) log = logging.getLogger('probert.storage') @@ -117,7 +117,7 @@ # update the size attr as it may only be the number # of blocks rather than size in bytes. attrs['size'] = \ - str(read_sys_block_size(device['DEVNAME'])) + str(read_sys_block_size_bytes(device['DEVNAME'])) blockdev[device['DEVNAME']] = dict(device) blockdev[device['DEVNAME']].update({'attrs': attrs}) # include partition table info if present @@ -145,6 +145,7 @@ probe_map = { 'bcache': bcache.probe, 'blockdev': blockdev_probe, + 'dasd': dasd.probe, 'dmcrypt': dmcrypt.probe, 'filesystem': filesystem.probe, 'lvm': lvm.probe, diff -Nru probert-0.0.17build2/probert/tests/data/dasdd.view probert-0.0.18/probert/tests/data/dasdd.view --- probert-0.0.17build2/probert/tests/data/dasdd.view 1970-01-01 00:00:00.000000000 +0000 +++ probert-0.0.18/probert/tests/data/dasdd.view 2020-02-25 21:14:40.000000000 +0000 @@ -0,0 +1,52 @@ + +--- general DASD information -------------------------------------------------- +device node : /dev/dasdd +busid : 0.0.1544 +type : ECKD +device type : hex 3390 dec 13200 + +--- DASD geometry ------------------------------------------------------------- +number of cylinders : hex 7563 dec 30051 +tracks per cylinder : hex f dec 15 +blocks per track : hex c dec 12 +blocksize : hex 1000 dec 4096 + +--- extended DASD information ------------------------------------------------- +real device number : hex 0 dec 0 +subchannel identifier : hex 1a4 dec 420 +CU type (SenseID) : hex 3990 dec 14736 +CU model (SenseID) : hex e9 dec 233 +device type (SenseID) : hex 3390 dec 13200 +device model (SenseID) : hex c dec 12 +open count : hex 2 dec 2 +req_queue_len : hex 0 dec 0 +chanq_len : hex 0 dec 0 +status : hex 5 dec 5 +label_block : hex 2 dec 2 +FBA_layout : hex 0 dec 0 +characteristics_size : hex 40 dec 64 +confdata_size : hex 100 dec 256 +format : hex 2 dec 2 CDL formatted +features : hex 0 dec 0 default + +characteristics : 3990e933 900c5e0c 39f72032 7563000f + e000e5a2 05940222 13090674 00000000 + 00000000 00000000 32321502 dfee0001 + 0677080f 007f4800 1f3c0000 00007563 + +configuration_data : dc010100 f0f0f2f1 f0f7f9f0 f0c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10844 + d4020000 f0f0f2f1 f0f7f9f6 f1c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10800 + d0000000 f0f0f2f1 f0f7f9f6 f1c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f00800 + f0000001 f0f0f2f1 f0f7f9f0 f0c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10800 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 81000003 2d001e00 15000247 000c0016 + 000cc044 0a0f61ca 00030000 0000a000 diff -Nru probert-0.0.17build2/probert/tests/data/dasde.view probert-0.0.18/probert/tests/data/dasde.view --- probert-0.0.17build2/probert/tests/data/dasde.view 1970-01-01 00:00:00.000000000 +0000 +++ probert-0.0.18/probert/tests/data/dasde.view 2020-02-25 21:14:40.000000000 +0000 @@ -0,0 +1,52 @@ + +--- general DASD information -------------------------------------------------- +device node : /dev/dasde +busid : 0.0.2520 +type : ECKD +device type : hex 3390 dec 13200 + +--- DASD geometry ------------------------------------------------------------- +number of cylinders : hex 2721 dec 10017 +tracks per cylinder : hex f dec 15 +blocks per track : hex 0 dec 0 +blocksize : hex 200 dec 512 + +--- extended DASD information ------------------------------------------------- +real device number : hex 0 dec 0 +subchannel identifier : hex 5e0 dec 1504 +CU type (SenseID) : hex 3990 dec 14736 +CU model (SenseID) : hex e9 dec 233 +device type (SenseID) : hex 3390 dec 13200 +device model (SenseID) : hex c dec 12 +open count : hex 1 dec 1 +req_queue_len : hex 0 dec 0 +chanq_len : hex 0 dec 0 +status : hex 3 dec 3 +label_block : hex 2 dec 2 +FBA_layout : hex 1 dec 1 +characteristics_size : hex 40 dec 64 +confdata_size : hex 100 dec 256 +format : hex 0 dec 0 NOT formatted +features : hex 0 dec 0 default + +characteristics : 3990e933 900c5e0c 39f72032 2721000f + e000e5a2 05940222 13090674 00000000 + 00000000 00000000 32321502 dfee0001 + 0677080f 007f4800 1f3c0000 00002721 + +configuration_data : dc010100 f0f0f2f1 f0f7f9f0 f0c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10920 + d4020000 f0f0f2f1 f0f7f9f6 f1c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10900 + d0000000 f0f0f2f1 f0f7f9f6 f1c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f00900 + f0000001 f0f0f2f1 f0f7f9f0 f0c9c2d4 + f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10900 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 00000000 00000000 00000000 00000000 + 81000003 2d001e00 25000246 000c0016 + 000cc020 84228958 00030000 0000a000 diff -Nru probert-0.0.17build2/probert/tests/helpers.py probert-0.0.18/probert/tests/helpers.py --- probert-0.0.17build2/probert/tests/helpers.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/tests/helpers.py 2020-02-25 21:14:40.000000000 +0000 @@ -13,10 +13,38 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import contextlib +import imp +import importlib +import mock import random import string +def builtin_module_name(): + options = ('builtins', '__builtin__') + for name in options: + try: + imp.find_module(name) + except ImportError: + continue + else: + print('importing and returning: %s' % name) + importlib.import_module(name) + return name + + +@contextlib.contextmanager +def simple_mocked_open(content=None): + if not content: + content = '' + m_open = mock.mock_open(read_data=content) + mod_name = builtin_module_name() + m_patch = '{}.open'.format(mod_name) + with mock.patch(m_patch, m_open, create=True): + yield m_open + + def random_string(length=8): """ return a random lowercase string with default length of 8""" return ''.join( diff -Nru probert-0.0.17build2/probert/tests/test_dasd.py probert-0.0.18/probert/tests/test_dasd.py --- probert-0.0.17build2/probert/tests/test_dasd.py 1970-01-01 00:00:00.000000000 +0000 +++ probert-0.0.18/probert/tests/test_dasd.py 2020-02-25 21:14:40.000000000 +0000 @@ -0,0 +1,171 @@ +import mock +import subprocess +import testtools + +from probert import dasd +from probert.tests import fakes +from probert.tests.helpers import random_string + + +class TestDasd(testtools.TestCase): + + def _load_test_data(self, data_fname): + testfile = fakes.TEST_DATA + '/' + data_fname + with open(testfile, 'r') as fh: + return fh.read() + + @mock.patch('probert.dasd.os.path.exists') + @mock.patch('probert.dasd.subprocess.run') + def test_dasdview_returns_stdout(self, m_run, m_exists): + devname = random_string() + dasdview_out = random_string() + cp = subprocess.CompletedProcess(args=['foo'], returncode=0, + stdout=dasdview_out.encode('utf-8'), + stderr="") + m_run.return_value = cp + m_exists.return_value = True + result = dasd.dasdview(devname) + self.assertEqual(dasdview_out, result) + m_run.assert_called_with(['dasdview', '--extended', devname], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) + + @mock.patch('probert.dasd.os.path.exists') + @mock.patch('probert.dasd.subprocess.run') + def test_dasdview_raises_valueerror(self, m_run, m_exists): + devname = random_string() + m_exists.return_value = False + self.assertRaises(ValueError, dasd.dasdview, devname) + self.assertEqual(0, m_run.call_count) + + @mock.patch('probert.dasd.os.path.exists') + @mock.patch('probert.dasd.subprocess.run') + def test_dasdview_returns_none_on_subprocess_error(self, m_run, m_exists): + devname = random_string() + m_exists.return_value = True + m_run.side_effect = subprocess.CalledProcessError( + cmd=[random_string()], returncode=1) + self.assertEqual(None, dasd.dasdview(devname)) + + def test_dasd_parses_blocksize(self): + self.assertEqual(4096, + dasd.blocksize(self._load_test_data('dasdd.view'))) + + def test_dasd_blocksize_returns_none_on_invalid_output(self): + self.assertIsNone(dasd.blocksize(random_string())) + + def test_dasd_parses_disk_format(self): + self.assertEqual('cdl', + dasd.disk_format(self._load_test_data('dasdd.view'))) + self.assertEqual('not-formatted', + dasd.disk_format(self._load_test_data('dasde.view'))) + + def test_dasd_parses_disk_format_ldl(self): + output = "format : hex 1 dec 1 LDL formatted" + self.assertEqual('ldl', dasd.disk_format(output)) + + def test_dasd_disk_format_returns_none_on_invalid_output(self): + self.assertIsNone(dasd.disk_format(random_string())) + + @mock.patch('probert.dasd.dasdview') + def test_get_dasd_info(self, m_dview): + devname = random_string() + id_path = random_string() + device = {'DEVNAME': devname, 'ID_PATH': 'ccw-' + id_path} + m_dview.return_value = self._load_test_data('dasdd.view') + self.assertEqual({'name': devname, 'device_id': id_path, + 'disk_layout': 'cdl', 'blocksize': 4096}, + dasd.get_dasd_info(device)) + + @mock.patch('probert.dasd.dasdview') + def test_get_dasd_info_returns_none_if_not_all(self, m_dview): + devname = random_string() + id_path = random_string() + device = {'DEVNAME': devname, 'ID_PATH': 'ccw-' + id_path} + m_dview.return_value = random_string() + self.assertIsNone(dasd.get_dasd_info(device)) + + @mock.patch('probert.dasd.blocksize') + @mock.patch('probert.dasd.dasdview') + def test_get_dasd_info_returns_none_if_bad_blocksize(self, m_dview, + m_block): + devname = random_string() + id_path = random_string() + device = {'DEVNAME': devname, 'ID_PATH': 'ccw-' + id_path} + m_dview.return_value = self._load_test_data('dasdd.view') + m_block.return_value = None + self.assertIsNone(dasd.get_dasd_info(device)) + + @mock.patch('probert.dasd.blocksize') + @mock.patch('probert.dasd.dasdview') + def test_get_dasd_info_returns_none_if_bad_disk_format(self, m_dview, + m_disk): + devname = random_string() + id_path = random_string() + device = {'DEVNAME': devname, 'ID_PATH': 'ccw-' + id_path} + m_dview.return_value = self._load_test_data('dasdd.view') + m_disk.return_value = None + self.assertIsNone(dasd.get_dasd_info(device)) + + @mock.patch('probert.dasd.platform.machine') + def test_dasd_probe_returns_empty_dict_non_s390x_arch(self, m_machine): + machine = random_string() + self.assertNotEqual("s390x", machine) + m_machine.return_value = machine + self.assertEqual({}, dasd.probe()) + + @mock.patch('probert.dasd.platform.machine') + @mock.patch('probert.dasd.dasdview') + def test_dasd_probe_dasdd(self, m_dasdview, m_machine): + m_machine.return_value = 's390x' + m_dasdview.side_effect = iter([self._load_test_data('dasdd.view')]) + + context = mock.MagicMock() + context.list_devices.side_effect = iter([ + [{"MAJOR": "94", "DEVNAME": "/dev/dasdd", "ID_SERIAL": "0X1544", + "ID_PATH": "ccw-0.0.1544"}], + ]) + expected_results = { + '/dev/dasdd': { + 'name': '/dev/dasdd', 'device_id': '0.0.1544', + 'disk_layout': 'cdl', 'blocksize': 4096}, + } + self.assertEqual(expected_results, dasd.probe(context=context)) + + @mock.patch('probert.dasd.platform.machine') + @mock.patch('probert.dasd.dasdview') + def test_dasd_probe_dasde(self, m_dasdview, m_machine): + m_machine.return_value = 's390x' + m_dasdview.side_effect = iter([self._load_test_data('dasde.view')]) + + context = mock.MagicMock() + context.list_devices.side_effect = iter([ + [{"MAJOR": "94", "DEVNAME": "/dev/dasde", + "ID_PATH": "ccw-0.0.2250"}], + ]) + expected_results = { + '/dev/dasde': { + 'name': '/dev/dasde', 'device_id': '0.0.2250', + 'disk_layout': 'not-formatted', 'blocksize': 512}, + } + self.assertEqual(expected_results, dasd.probe(context=context)) + + @mock.patch('probert.dasd.platform.machine') + @mock.patch('probert.dasd.dasdview') + def test_dasd_probe_dasdd_skips_partitions(self, m_dasdview, m_machine): + m_machine.return_value = 's390x' + m_dasdview.side_effect = iter([self._load_test_data('dasdd.view')]) + + context = mock.MagicMock() + context.list_devices.side_effect = iter([ + [{"MAJOR": "94", "DEVNAME": "/dev/dasdd", "ID_SERIAL": "0X1544", + "ID_PATH": "ccw-0.0.1544"}], + [{"MAJOR": "94", "DEVNAME": "/dev/dasdd1", "ID_SERIAL": "0X1544", + "ID_PATH": "ccw-0.0.1544", "PARTN": "1"}], + ]) + expected_results = { + '/dev/dasdd': { + 'name': '/dev/dasdd', 'device_id': '0.0.1544', + 'disk_layout': 'cdl', 'blocksize': 4096}, + } + self.assertEqual(expected_results, dasd.probe(context=context)) diff -Nru probert-0.0.17build2/probert/tests/test_lvm.py probert-0.0.18/probert/tests/test_lvm.py --- probert-0.0.17build2/probert/tests/test_lvm.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/tests/test_lvm.py 2020-02-25 21:14:40.000000000 +0000 @@ -244,7 +244,7 @@ 'size': '0B'}), lvm.extract_lvm_volgroup('vg0', input_data)) - @mock.patch('probert.lvm.read_sys_block_size') + @mock.patch('probert.lvm.read_sys_block_size_bytes') def test_extract_lvm_partition(self, m_size, m_run): size = 100000000 m_size.return_value = size @@ -261,7 +261,7 @@ lvm.extract_lvm_partition(input_data)) m_size.assert_called_with('/dev/dm-2') - @mock.patch('probert.lvm.read_sys_block_size') + @mock.patch('probert.lvm.read_sys_block_size_bytes') @mock.patch('probert.lvm.activate_volgroups') @mock.patch('probert.lvm.lvm_scan') @mock.patch('probert.lvm.pyudev.Context.list_devices') @@ -294,7 +294,7 @@ } self.assertEqual(expected_result, lvm.probe()) - @mock.patch('probert.lvm.read_sys_block_size') + @mock.patch('probert.lvm.read_sys_block_size_bytes') @mock.patch('probert.lvm.activate_volgroups') @mock.patch('probert.lvm.lvm_scan') @mock.patch('probert.lvm.pyudev.Context.list_devices') diff -Nru probert-0.0.17build2/probert/tests/test_utils.py probert-0.0.18/probert/tests/test_utils.py --- probert-0.0.17build2/probert/tests/test_utils.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/tests/test_utils.py 2020-02-25 21:14:40.000000000 +0000 @@ -1,6 +1,8 @@ import testtools +from mock import call from probert import utils +from probert.tests.helpers import random_string, simple_mocked_open class ProbertTestUtils(testtools.TestCase): @@ -35,3 +37,23 @@ } test_result = utils.dict_merge(r1, r2) self.assertEqual(sorted(combined), sorted(test_result)) + + def test_utils_read_sys_block_size_bytes(self): + devname = random_string() + expected_fname = '/sys/class/block/%s/size' % devname + expected_bytes = 10737418240 + content = '20971520' + with simple_mocked_open(content=content) as m_open: + result = utils.read_sys_block_size_bytes(devname) + self.assertEqual(expected_bytes, result) + self.assertEqual([call(expected_fname)], m_open.call_args_list) + + def test_utils_read_sys_block_size_bytes_strips_value(self): + devname = random_string() + expected_fname = '/sys/class/block/%s/size' % devname + expected_bytes = 10737418240 + content = ' 20971520 \n ' + with simple_mocked_open(content=content) as m_open: + result = utils.read_sys_block_size_bytes(devname) + self.assertEqual(expected_bytes, result) + self.assertEqual([call(expected_fname)], m_open.call_args_list) diff -Nru probert-0.0.17build2/probert/utils.py probert-0.0.18/probert/utils.py --- probert-0.0.17build2/probert/utils.py 2019-08-08 13:49:43.000000000 +0000 +++ probert-0.0.18/probert/utils.py 2020-02-25 21:14:40.000000000 +0000 @@ -22,6 +22,10 @@ "bridge_hello", "bridge_maxage", "bridge_maxwait", "bridge_stp", ] +# sysfs size attribute is always in 512-byte units +# https://github.com/torvalds/linux/blob/6f0d349d922ba44e4348a17a78ea51b7135965b1/include/linux/types.h#L125 +SECTOR_SIZE_BYTES = 512 + # from juju-deployer utils.relation_merge def dict_merge(onto, source): @@ -225,21 +229,12 @@ ifaces[iface]['auto'] = False -def read_sys_block_size(device): +def read_sys_block_size_bytes(device): + """ /sys/class/block//size and return integer value in bytes""" device_dir = os.path.join('/sys/class/block', os.path.basename(device)) blockdev_size = os.path.join(device_dir, 'size') with open(blockdev_size) as d: - size = int(d.read().strip()) - - logsize_base = device_dir - if not os.path.exists(os.path.join(device_dir, 'queue')): - parent_dev = os.path.basename(re.split(r'[\d+]', device)[0]) - logsize_base = os.path.join('/sys/class/block', parent_dev) - - logical_size = os.path.join(logsize_base, 'queue', 'logical_block_size') - if os.path.exists(logical_size): - with open(logical_size) as s: - size *= int(s.read().strip()) + size = int(d.read().strip()) * SECTOR_SIZE_BYTES return size