|
|
ca58b3 |
From 1c985230cd8559c3fc4af33f9bff6e2c103ce5e9 Mon Sep 17 00:00:00 2001
|
|
|
ca58b3 |
From: Eduardo Otubo <otubo@redhat.com>
|
|
|
ca58b3 |
Date: Wed, 26 Sep 2018 13:57:40 +0200
|
|
|
ca58b3 |
Subject: [PATCH 2/4] Azure: Ignore NTFS mount errors when checking ephemeral
|
|
|
ca58b3 |
drive
|
|
|
ca58b3 |
|
|
|
ca58b3 |
RH-Author: Eduardo Otubo <otubo@redhat.com>
|
|
|
ca58b3 |
Message-id: <20180926135742.11140-3-otubo@redhat.com>
|
|
|
ca58b3 |
Patchwork-id: 82300
|
|
|
ca58b3 |
O-Subject: [RHEL-7.6 cloud-init PATCHv2 2/4] Azure: Ignore NTFS mount errors when checking ephemeral drive
|
|
|
ca58b3 |
Bugzilla: 1560415
|
|
|
ca58b3 |
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
|
|
ca58b3 |
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
ca58b3 |
|
|
|
ca58b3 |
commit aa4eeb80839382117e1813e396dc53aa634fd7ba
|
|
|
ca58b3 |
Author: Paul Meyer <paulmey@microsoft.com>
|
|
|
ca58b3 |
Date: Wed May 23 15:45:39 2018 -0400
|
|
|
ca58b3 |
|
|
|
ca58b3 |
Azure: Ignore NTFS mount errors when checking ephemeral drive
|
|
|
ca58b3 |
|
|
|
ca58b3 |
The Azure data source provides a method to check whether a NTFS partition
|
|
|
ca58b3 |
on the ephemeral disk is safe for reformatting to ext4. The method checks
|
|
|
ca58b3 |
to see if there are customer data files on the disk. However, mounting
|
|
|
ca58b3 |
the partition fails on systems that do not have the capability of
|
|
|
ca58b3 |
mounting NTFS. Note that in this case, it is also very unlikely that the
|
|
|
ca58b3 |
NTFS partition would have been used by the system (since it can't mount
|
|
|
ca58b3 |
it). The only case would be where an update to the system removed the
|
|
|
ca58b3 |
capability to mount NTFS, the likelihood of which is also very small.
|
|
|
ca58b3 |
This change allows the reformatting of the ephemeral disk to ext4 on
|
|
|
ca58b3 |
systems where mounting NTFS is not supported.
|
|
|
ca58b3 |
|
|
|
ca58b3 |
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
|
|
|
ca58b3 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
ca58b3 |
---
|
|
|
ca58b3 |
cloudinit/sources/DataSourceAzure.py | 63 ++++++++++++----
|
|
|
ca58b3 |
cloudinit/util.py | 5 +-
|
|
|
ca58b3 |
tests/unittests/test_datasource/test_azure.py | 105 +++++++++++++++++++++-----
|
|
|
ca58b3 |
3 files changed, 138 insertions(+), 35 deletions(-)
|
|
|
ca58b3 |
|
|
|
ca58b3 |
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
|
|
|
ca58b3 |
index 23b4d53..7e49455 100644
|
|
|
ca58b3 |
--- a/cloudinit/sources/DataSourceAzure.py
|
|
|
ca58b3 |
+++ b/cloudinit/sources/DataSourceAzure.py
|
|
|
ca58b3 |
@@ -214,6 +214,7 @@ BUILTIN_CLOUD_CONFIG = {
|
|
|
ca58b3 |
}
|
|
|
ca58b3 |
|
|
|
ca58b3 |
DS_CFG_PATH = ['datasource', DS_NAME]
|
|
|
ca58b3 |
+DS_CFG_KEY_PRESERVE_NTFS = 'never_destroy_ntfs'
|
|
|
ca58b3 |
DEF_EPHEMERAL_LABEL = 'Temporary Storage'
|
|
|
ca58b3 |
|
|
|
ca58b3 |
# The redacted password fails to meet password complexity requirements
|
|
|
ca58b3 |
@@ -400,14 +401,9 @@ class DataSourceAzure(sources.DataSource):
|
|
|
ca58b3 |
if found == ddir:
|
|
|
ca58b3 |
LOG.debug("using files cached in %s", ddir)
|
|
|
ca58b3 |
|
|
|
ca58b3 |
- # azure / hyper-v provides random data here
|
|
|
ca58b3 |
- # TODO. find the seed on FreeBSD platform
|
|
|
ca58b3 |
- # now update ds_cfg to reflect contents pass in config
|
|
|
ca58b3 |
- if not util.is_FreeBSD():
|
|
|
ca58b3 |
- seed = util.load_file("/sys/firmware/acpi/tables/OEM0",
|
|
|
ca58b3 |
- quiet=True, decode=False)
|
|
|
ca58b3 |
- if seed:
|
|
|
ca58b3 |
- self.metadata['random_seed'] = seed
|
|
|
ca58b3 |
+ seed = _get_random_seed()
|
|
|
ca58b3 |
+ if seed:
|
|
|
ca58b3 |
+ self.metadata['random_seed'] = seed
|
|
|
ca58b3 |
|
|
|
ca58b3 |
user_ds_cfg = util.get_cfg_by_path(self.cfg, DS_CFG_PATH, {})
|
|
|
ca58b3 |
self.ds_cfg = util.mergemanydict([user_ds_cfg, self.ds_cfg])
|
|
|
ca58b3 |
@@ -537,7 +533,9 @@ class DataSourceAzure(sources.DataSource):
|
|
|
ca58b3 |
return fabric_data
|
|
|
ca58b3 |
|
|
|
ca58b3 |
def activate(self, cfg, is_new_instance):
|
|
|
ca58b3 |
- address_ephemeral_resize(is_new_instance=is_new_instance)
|
|
|
ca58b3 |
+ address_ephemeral_resize(is_new_instance=is_new_instance,
|
|
|
ca58b3 |
+ preserve_ntfs=self.ds_cfg.get(
|
|
|
ca58b3 |
+ DS_CFG_KEY_PRESERVE_NTFS, False))
|
|
|
ca58b3 |
return
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@property
|
|
|
ca58b3 |
@@ -581,17 +579,29 @@ def _has_ntfs_filesystem(devpath):
|
|
|
ca58b3 |
return os.path.realpath(devpath) in ntfs_devices
|
|
|
ca58b3 |
|
|
|
ca58b3 |
|
|
|
ca58b3 |
-def can_dev_be_reformatted(devpath):
|
|
|
ca58b3 |
- """Determine if block device devpath is newly formatted ephemeral.
|
|
|
ca58b3 |
+def can_dev_be_reformatted(devpath, preserve_ntfs):
|
|
|
ca58b3 |
+ """Determine if the ephemeral drive at devpath should be reformatted.
|
|
|
ca58b3 |
|
|
|
ca58b3 |
- A newly formatted disk will:
|
|
|
ca58b3 |
+ A fresh ephemeral disk is formatted by Azure and will:
|
|
|
ca58b3 |
a.) have a partition table (dos or gpt)
|
|
|
ca58b3 |
b.) have 1 partition that is ntfs formatted, or
|
|
|
ca58b3 |
have 2 partitions with the second partition ntfs formatted.
|
|
|
ca58b3 |
(larger instances with >2TB ephemeral disk have gpt, and will
|
|
|
ca58b3 |
have a microsoft reserved partition as part 1. LP: #1686514)
|
|
|
ca58b3 |
c.) the ntfs partition will have no files other than possibly
|
|
|
ca58b3 |
- 'dataloss_warning_readme.txt'"""
|
|
|
ca58b3 |
+ 'dataloss_warning_readme.txt'
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
+ User can indicate that NTFS should never be destroyed by setting
|
|
|
ca58b3 |
+ DS_CFG_KEY_PRESERVE_NTFS in dscfg.
|
|
|
ca58b3 |
+ If data is found on NTFS, user is warned to set DS_CFG_KEY_PRESERVE_NTFS
|
|
|
ca58b3 |
+ to make sure cloud-init does not accidentally wipe their data.
|
|
|
ca58b3 |
+ If cloud-init cannot mount the disk to check for data, destruction
|
|
|
ca58b3 |
+ will be allowed, unless the dscfg key is set."""
|
|
|
ca58b3 |
+ if preserve_ntfs:
|
|
|
ca58b3 |
+ msg = ('config says to never destroy NTFS (%s.%s), skipping checks' %
|
|
|
ca58b3 |
+ (".".join(DS_CFG_PATH), DS_CFG_KEY_PRESERVE_NTFS))
|
|
|
ca58b3 |
+ return False, msg
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
if not os.path.exists(devpath):
|
|
|
ca58b3 |
return False, 'device %s does not exist' % devpath
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -624,18 +634,27 @@ def can_dev_be_reformatted(devpath):
|
|
|
ca58b3 |
bmsg = ('partition %s (%s) on device %s was ntfs formatted' %
|
|
|
ca58b3 |
(cand_part, cand_path, devpath))
|
|
|
ca58b3 |
try:
|
|
|
ca58b3 |
- file_count = util.mount_cb(cand_path, count_files)
|
|
|
ca58b3 |
+ file_count = util.mount_cb(cand_path, count_files, mtype="ntfs",
|
|
|
ca58b3 |
+ update_env_for_mount={'LANG': 'C'})
|
|
|
ca58b3 |
except util.MountFailedError as e:
|
|
|
ca58b3 |
+ if "mount: unknown filesystem type 'ntfs'" in str(e):
|
|
|
ca58b3 |
+ return True, (bmsg + ' but this system cannot mount NTFS,'
|
|
|
ca58b3 |
+ ' assuming there are no important files.'
|
|
|
ca58b3 |
+ ' Formatting allowed.')
|
|
|
ca58b3 |
return False, bmsg + ' but mount of %s failed: %s' % (cand_part, e)
|
|
|
ca58b3 |
|
|
|
ca58b3 |
if file_count != 0:
|
|
|
ca58b3 |
+ LOG.warning("it looks like you're using NTFS on the ephemeral disk, "
|
|
|
ca58b3 |
+ 'to ensure that filesystem does not get wiped, set '
|
|
|
ca58b3 |
+ '%s.%s in config', '.'.join(DS_CFG_PATH),
|
|
|
ca58b3 |
+ DS_CFG_KEY_PRESERVE_NTFS)
|
|
|
ca58b3 |
return False, bmsg + ' but had %d files on it.' % file_count
|
|
|
ca58b3 |
|
|
|
ca58b3 |
return True, bmsg + ' and had no important files. Safe for reformatting.'
|
|
|
ca58b3 |
|
|
|
ca58b3 |
|
|
|
ca58b3 |
def address_ephemeral_resize(devpath=RESOURCE_DISK_PATH, maxwait=120,
|
|
|
ca58b3 |
- is_new_instance=False):
|
|
|
ca58b3 |
+ is_new_instance=False, preserve_ntfs=False):
|
|
|
ca58b3 |
# wait for ephemeral disk to come up
|
|
|
ca58b3 |
naplen = .2
|
|
|
ca58b3 |
missing = util.wait_for_files([devpath], maxwait=maxwait, naplen=naplen,
|
|
|
ca58b3 |
@@ -651,7 +670,7 @@ def address_ephemeral_resize(devpath=RESOURCE_DISK_PATH, maxwait=120,
|
|
|
ca58b3 |
if is_new_instance:
|
|
|
ca58b3 |
result, msg = (True, "First instance boot.")
|
|
|
ca58b3 |
else:
|
|
|
ca58b3 |
- result, msg = can_dev_be_reformatted(devpath)
|
|
|
ca58b3 |
+ result, msg = can_dev_be_reformatted(devpath, preserve_ntfs)
|
|
|
ca58b3 |
|
|
|
ca58b3 |
LOG.debug("reformattable=%s: %s", result, msg)
|
|
|
ca58b3 |
if not result:
|
|
|
ca58b3 |
@@ -965,6 +984,18 @@ def _check_freebsd_cdrom(cdrom_dev):
|
|
|
ca58b3 |
return False
|
|
|
ca58b3 |
|
|
|
ca58b3 |
|
|
|
ca58b3 |
+def _get_random_seed():
|
|
|
ca58b3 |
+ """Return content random seed file if available, otherwise,
|
|
|
ca58b3 |
+ return None."""
|
|
|
ca58b3 |
+ # azure / hyper-v provides random data here
|
|
|
ca58b3 |
+ # TODO. find the seed on FreeBSD platform
|
|
|
ca58b3 |
+ # now update ds_cfg to reflect contents pass in config
|
|
|
ca58b3 |
+ if util.is_FreeBSD():
|
|
|
ca58b3 |
+ return None
|
|
|
ca58b3 |
+ return util.load_file("/sys/firmware/acpi/tables/OEM0",
|
|
|
ca58b3 |
+ quiet=True, decode=False)
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
def list_possible_azure_ds_devs():
|
|
|
ca58b3 |
devlist = []
|
|
|
ca58b3 |
if util.is_FreeBSD():
|
|
|
ca58b3 |
diff --git a/cloudinit/util.py b/cloudinit/util.py
|
|
|
ca58b3 |
index 0ab2c48..c8e14ba 100644
|
|
|
ca58b3 |
--- a/cloudinit/util.py
|
|
|
ca58b3 |
+++ b/cloudinit/util.py
|
|
|
ca58b3 |
@@ -1608,7 +1608,8 @@ def mounts():
|
|
|
ca58b3 |
return mounted
|
|
|
ca58b3 |
|
|
|
ca58b3 |
|
|
|
ca58b3 |
-def mount_cb(device, callback, data=None, rw=False, mtype=None, sync=True):
|
|
|
ca58b3 |
+def mount_cb(device, callback, data=None, rw=False, mtype=None, sync=True,
|
|
|
ca58b3 |
+ update_env_for_mount=None):
|
|
|
ca58b3 |
"""
|
|
|
ca58b3 |
Mount the device, call method 'callback' passing the directory
|
|
|
ca58b3 |
in which it was mounted, then unmount. Return whatever 'callback'
|
|
|
ca58b3 |
@@ -1670,7 +1671,7 @@ def mount_cb(device, callback, data=None, rw=False, mtype=None, sync=True):
|
|
|
ca58b3 |
mountcmd.extend(['-t', mtype])
|
|
|
ca58b3 |
mountcmd.append(device)
|
|
|
ca58b3 |
mountcmd.append(tmpd)
|
|
|
ca58b3 |
- subp(mountcmd)
|
|
|
ca58b3 |
+ subp(mountcmd, update_env=update_env_for_mount)
|
|
|
ca58b3 |
umount = tmpd # This forces it to be unmounted (when set)
|
|
|
ca58b3 |
mountpoint = tmpd
|
|
|
ca58b3 |
break
|
|
|
ca58b3 |
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
|
|
|
ca58b3 |
index 3e8b791..af2c93a 100644
|
|
|
ca58b3 |
--- a/tests/unittests/test_datasource/test_azure.py
|
|
|
ca58b3 |
+++ b/tests/unittests/test_datasource/test_azure.py
|
|
|
ca58b3 |
@@ -1,10 +1,10 @@
|
|
|
ca58b3 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
|
ca58b3 |
|
|
|
ca58b3 |
from cloudinit import helpers
|
|
|
ca58b3 |
-from cloudinit.util import b64e, decode_binary, load_file, write_file
|
|
|
ca58b3 |
from cloudinit.sources import DataSourceAzure as dsaz
|
|
|
ca58b3 |
-from cloudinit.util import find_freebsd_part
|
|
|
ca58b3 |
-from cloudinit.util import get_path_dev_freebsd
|
|
|
ca58b3 |
+from cloudinit.util import (b64e, decode_binary, load_file, write_file,
|
|
|
ca58b3 |
+ find_freebsd_part, get_path_dev_freebsd,
|
|
|
ca58b3 |
+ MountFailedError)
|
|
|
ca58b3 |
from cloudinit.version import version_string as vs
|
|
|
ca58b3 |
from cloudinit.tests.helpers import (CiTestCase, TestCase, populate_dir, mock,
|
|
|
ca58b3 |
ExitStack, PY26, SkipTest)
|
|
|
ca58b3 |
@@ -95,6 +95,8 @@ class TestAzureDataSource(CiTestCase):
|
|
|
ca58b3 |
self.patches = ExitStack()
|
|
|
ca58b3 |
self.addCleanup(self.patches.close)
|
|
|
ca58b3 |
|
|
|
ca58b3 |
+ self.patches.enter_context(mock.patch.object(dsaz, '_get_random_seed'))
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
super(TestAzureDataSource, self).setUp()
|
|
|
ca58b3 |
|
|
|
ca58b3 |
def apply_patches(self, patches):
|
|
|
ca58b3 |
@@ -335,6 +337,18 @@ fdescfs /dev/fd fdescfs rw 0 0
|
|
|
ca58b3 |
self.assertTrue(ret)
|
|
|
ca58b3 |
self.assertEqual(data['agent_invoked'], '_COMMAND')
|
|
|
ca58b3 |
|
|
|
ca58b3 |
+ def test_sys_cfg_set_never_destroy_ntfs(self):
|
|
|
ca58b3 |
+ sys_cfg = {'datasource': {'Azure': {
|
|
|
ca58b3 |
+ 'never_destroy_ntfs': 'user-supplied-value'}}}
|
|
|
ca58b3 |
+ data = {'ovfcontent': construct_valid_ovf_env(data={}),
|
|
|
ca58b3 |
+ 'sys_cfg': sys_cfg}
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
+ dsrc = self._get_ds(data)
|
|
|
ca58b3 |
+ ret = self._get_and_setup(dsrc)
|
|
|
ca58b3 |
+ self.assertTrue(ret)
|
|
|
ca58b3 |
+ self.assertEqual(dsrc.ds_cfg.get(dsaz.DS_CFG_KEY_PRESERVE_NTFS),
|
|
|
ca58b3 |
+ 'user-supplied-value')
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
def test_username_used(self):
|
|
|
ca58b3 |
odata = {'HostName': "myhost", 'UserName': "myuser"}
|
|
|
ca58b3 |
data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
|
|
|
ca58b3 |
@@ -676,6 +690,8 @@ class TestAzureBounce(CiTestCase):
|
|
|
ca58b3 |
mock.MagicMock(return_value={})))
|
|
|
ca58b3 |
self.patches.enter_context(
|
|
|
ca58b3 |
mock.patch.object(dsaz.util, 'which', lambda x: True))
|
|
|
ca58b3 |
+ self.patches.enter_context(
|
|
|
ca58b3 |
+ mock.patch.object(dsaz, '_get_random_seed'))
|
|
|
ca58b3 |
|
|
|
ca58b3 |
def _dmi_mocks(key):
|
|
|
ca58b3 |
if key == 'system-uuid':
|
|
|
ca58b3 |
@@ -957,7 +973,9 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
# return sorted by partition number
|
|
|
ca58b3 |
return sorted(ret, key=lambda d: d[0])
|
|
|
ca58b3 |
|
|
|
ca58b3 |
- def mount_cb(device, callback):
|
|
|
ca58b3 |
+ def mount_cb(device, callback, mtype, update_env_for_mount):
|
|
|
ca58b3 |
+ self.assertEqual('ntfs', mtype)
|
|
|
ca58b3 |
+ self.assertEqual('C', update_env_for_mount.get('LANG'))
|
|
|
ca58b3 |
p = self.tmp_dir()
|
|
|
ca58b3 |
for f in bypath.get(device).get('files', []):
|
|
|
ca58b3 |
write_file(os.path.join(p, f), content=f)
|
|
|
ca58b3 |
@@ -988,14 +1006,16 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'/dev/sda2': {'num': 2},
|
|
|
ca58b3 |
'/dev/sda3': {'num': 3},
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertFalse(value)
|
|
|
ca58b3 |
self.assertIn("3 or more", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
def test_no_partitions_is_false(self):
|
|
|
ca58b3 |
"""A disk with no partitions can not be formatted."""
|
|
|
ca58b3 |
self.patchup({'/dev/sda': {}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertFalse(value)
|
|
|
ca58b3 |
self.assertIn("not partitioned", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1007,7 +1027,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'/dev/sda1': {'num': 1},
|
|
|
ca58b3 |
'/dev/sda2': {'num': 2, 'fs': 'ext4', 'files': []},
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertFalse(value)
|
|
|
ca58b3 |
self.assertIn("not ntfs", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1020,7 +1041,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'/dev/sda2': {'num': 2, 'fs': 'ntfs',
|
|
|
ca58b3 |
'files': ['secret.txt']},
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertFalse(value)
|
|
|
ca58b3 |
self.assertIn("files on it", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1032,7 +1054,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'/dev/sda1': {'num': 1},
|
|
|
ca58b3 |
'/dev/sda2': {'num': 2, 'fs': 'ntfs', 'files': []},
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertTrue(value)
|
|
|
ca58b3 |
self.assertIn("safe for", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1043,7 +1066,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'partitions': {
|
|
|
ca58b3 |
'/dev/sda1': {'num': 1, 'fs': 'zfs'},
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertFalse(value)
|
|
|
ca58b3 |
self.assertIn("not ntfs", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1055,9 +1079,14 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'/dev/sda1': {'num': 1, 'fs': 'ntfs',
|
|
|
ca58b3 |
'files': ['file1.txt', 'file2.exe']},
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
- self.assertFalse(value)
|
|
|
ca58b3 |
- self.assertIn("files on it", msg.lower())
|
|
|
ca58b3 |
+ with mock.patch.object(dsaz.LOG, 'warning') as warning:
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
+ wmsg = warning.call_args[0][0]
|
|
|
ca58b3 |
+ self.assertIn("looks like you're using NTFS on the ephemeral disk",
|
|
|
ca58b3 |
+ wmsg)
|
|
|
ca58b3 |
+ self.assertFalse(value)
|
|
|
ca58b3 |
+ self.assertIn("files on it", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
def test_one_partition_ntfs_empty_is_true(self):
|
|
|
ca58b3 |
"""1 mountable ntfs partition and no files can be formatted."""
|
|
|
ca58b3 |
@@ -1066,7 +1095,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'partitions': {
|
|
|
ca58b3 |
'/dev/sda1': {'num': 1, 'fs': 'ntfs', 'files': []}
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertTrue(value)
|
|
|
ca58b3 |
self.assertIn("safe for", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1078,7 +1108,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'/dev/sda1': {'num': 1, 'fs': 'ntfs',
|
|
|
ca58b3 |
'files': ['dataloss_warning_readme.txt']}
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted("/dev/sda")
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertTrue(value)
|
|
|
ca58b3 |
self.assertIn("safe for", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1093,7 +1124,8 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
'num': 1, 'fs': 'ntfs', 'files': [self.warning_file],
|
|
|
ca58b3 |
'realpath': '/dev/sdb1'}
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted(epath)
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted(epath,
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertTrue(value)
|
|
|
ca58b3 |
self.assertIn("safe for", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
@@ -1112,10 +1144,49 @@ class TestCanDevBeReformatted(CiTestCase):
|
|
|
ca58b3 |
epath + '-part3': {'num': 3, 'fs': 'ext',
|
|
|
ca58b3 |
'realpath': '/dev/sdb3'}
|
|
|
ca58b3 |
}}})
|
|
|
ca58b3 |
- value, msg = dsaz.can_dev_be_reformatted(epath)
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted(epath,
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
self.assertFalse(value)
|
|
|
ca58b3 |
self.assertIn("3 or more", msg.lower())
|
|
|
ca58b3 |
|
|
|
ca58b3 |
+ def test_ntfs_mount_errors_true(self):
|
|
|
ca58b3 |
+ """can_dev_be_reformatted does not fail if NTFS is unknown fstype."""
|
|
|
ca58b3 |
+ self.patchup({
|
|
|
ca58b3 |
+ '/dev/sda': {
|
|
|
ca58b3 |
+ 'partitions': {
|
|
|
ca58b3 |
+ '/dev/sda1': {'num': 1, 'fs': 'ntfs', 'files': []}
|
|
|
ca58b3 |
+ }}})
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
+ err = ("Unexpected error while running command.\n",
|
|
|
ca58b3 |
+ "Command: ['mount', '-o', 'ro,sync', '-t', 'auto', ",
|
|
|
ca58b3 |
+ "'/dev/sda1', '/fake-tmp/dir']\n"
|
|
|
ca58b3 |
+ "Exit code: 32\n"
|
|
|
ca58b3 |
+ "Reason: -\n"
|
|
|
ca58b3 |
+ "Stdout: -\n"
|
|
|
ca58b3 |
+ "Stderr: mount: unknown filesystem type 'ntfs'")
|
|
|
ca58b3 |
+ self.m_mount_cb.side_effect = MountFailedError(
|
|
|
ca58b3 |
+ 'Failed mounting %s to %s due to: %s' %
|
|
|
ca58b3 |
+ ('/dev/sda', '/fake-tmp/dir', err))
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted('/dev/sda',
|
|
|
ca58b3 |
+ preserve_ntfs=False)
|
|
|
ca58b3 |
+ self.assertTrue(value)
|
|
|
ca58b3 |
+ self.assertIn('cannot mount NTFS, assuming', msg)
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
+ def test_never_destroy_ntfs_config_false(self):
|
|
|
ca58b3 |
+ """Normally formattable situation with never_destroy_ntfs set."""
|
|
|
ca58b3 |
+ self.patchup({
|
|
|
ca58b3 |
+ '/dev/sda': {
|
|
|
ca58b3 |
+ 'partitions': {
|
|
|
ca58b3 |
+ '/dev/sda1': {'num': 1, 'fs': 'ntfs',
|
|
|
ca58b3 |
+ 'files': ['dataloss_warning_readme.txt']}
|
|
|
ca58b3 |
+ }}})
|
|
|
ca58b3 |
+ value, msg = dsaz.can_dev_be_reformatted("/dev/sda",
|
|
|
ca58b3 |
+ preserve_ntfs=True)
|
|
|
ca58b3 |
+ self.assertFalse(value)
|
|
|
ca58b3 |
+ self.assertIn("config says to never destroy NTFS "
|
|
|
ca58b3 |
+ "(datasource.Azure.never_destroy_ntfs)", msg)
|
|
|
ca58b3 |
+
|
|
|
ca58b3 |
|
|
|
ca58b3 |
class TestAzureNetExists(CiTestCase):
|
|
|
ca58b3 |
|
|
|
ca58b3 |
--
|
|
|
ca58b3 |
1.8.3.1
|
|
|
ca58b3 |
|