~smoser/curtin/yakkety.lp1666986

« back to all changes in this revision

Viewing changes to tests/unittests/test_clear_holders.py

  • Committer: Ryan Harper
  • Date: 2016-09-29 18:36:01 UTC
  • mfrom: (1.1.41)
  • mto: This revision was merged to the branch mainline in revision 59.
  • Revision ID: ryan.harper@canonical.com-20160929183601-utf54hgpsizdfzxp
* New upstream snapshot.
  - unittest,tox.ini: catch and fix issue with trusty-level mock of open 
  - block/mdadm: add option to ignore mdadm_assemble errors  (LP: #1618429)
  - curtin/doc: overhaul curtin documentation for readthedocs.org  (LP: #1351085)
  - curtin.util: re-add support for RunInChroot  (LP: #1617375)
  - curtin/net: overhaul of eni rendering to handle mixed ipv4/ipv6 configs 
  - curtin.block: refactor clear_holders logic into block.clear_holders and cli cmd 
  - curtin.apply_net should exit non-zero upon exception.  (LP: #1615780)
  - apt: fix bug in disable_suites if sources.list line is blank. 
  - vmtests: disable Wily in vmtests 
  - Fix the unittests for test_apt_source. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from unittest import TestCase
 
2
import mock
 
3
 
 
4
from curtin.block import clear_holders
 
5
import os
 
6
import textwrap
 
7
 
 
8
 
 
9
class TestClearHolders(TestCase):
 
10
    test_blockdev = '/dev/null'
 
11
    test_syspath = '/sys/class/block/null'
 
12
    example_holders_trees = [
 
13
        [{'device': '/sys/class/block/sda', 'name': 'sda', 'holders':
 
14
          [{'device': '/sys/class/block/sda/sda1', 'name': 'sda1',
 
15
            'holders': [], 'dev_type': 'partition'},
 
16
           {'device': '/sys/class/block/sda/sda2', 'name': 'sda2',
 
17
            'holders': [], 'dev_type': 'partition'},
 
18
           {'device': '/sys/class/block/sda/sda5', 'name': 'sda5', 'holders':
 
19
            [{'device': '/sys/class/block/dm-0', 'name': 'dm-0', 'holders':
 
20
              [{'device': '/sys/class/block/dm-1', 'name': 'dm-1',
 
21
                'holders': [], 'dev_type': 'lvm'},
 
22
               {'device': '/sys/class/block/dm-2', 'name': 'dm-2', 'holders':
 
23
                [{'device': '/sys/class/block/dm-3', 'name': 'dm-3',
 
24
                  'holders': [], 'dev_type': 'crypt'}],
 
25
                'dev_type': 'lvm'}],
 
26
              'dev_type': 'crypt'}],
 
27
            'dev_type': 'partition'}],
 
28
          'dev_type': 'disk'}],
 
29
        [{"device": "/sys/class/block/vdb", 'name': 'vdb', "holders":
 
30
          [{"device": "/sys/class/block/vdb/vdb1", 'name': 'vdb1',
 
31
            "holders": [], "dev_type": "partition"},
 
32
           {"device": "/sys/class/block/vdb/vdb2", 'name': 'vdb2',
 
33
            "holders": [], "dev_type": "partition"},
 
34
           {"device": "/sys/class/block/vdb/vdb3", 'name': 'vdb3', "holders":
 
35
            [{"device": "/sys/class/block/md0", 'name': 'md0', "holders":
 
36
              [{"device": "/sys/class/block/bcache1", 'name': 'bcache1',
 
37
                "holders": [], "dev_type": "bcache"}],
 
38
              "dev_type": "raid"}],
 
39
            "dev_type": "partition"},
 
40
           {"device": "/sys/class/block/vdb/vdb4", 'name': 'vdb4', "holders":
 
41
            [{"device": "/sys/class/block/md0", 'name': 'md0', "holders":
 
42
              [{"device": "/sys/class/block/bcache1", 'name': 'bcache1',
 
43
                "holders": [], "dev_type": "bcache"}],
 
44
              "dev_type": "raid"}],
 
45
            "dev_type": "partition"},
 
46
           {"device": "/sys/class/block/vdb/vdb5", 'name': 'vdb5', "holders":
 
47
            [{"device": "/sys/class/block/md0", 'name': 'md0', "holders":
 
48
              [{"device": "/sys/class/block/bcache1", 'name': 'bcache1',
 
49
                "holders": [], "dev_type": "bcache"}],
 
50
              "dev_type": "raid"}],
 
51
            "dev_type": "partition"},
 
52
           {"device": "/sys/class/block/vdb/vdb6", 'name': 'vdb6', "holders":
 
53
            [{"device": "/sys/class/block/bcache1", 'name': 'bcache1',
 
54
              "holders": [], "dev_type": "bcache"},
 
55
             {"device": "/sys/class/block/bcache2", 'name': 'bcache2',
 
56
              "holders": [], "dev_type": "bcache"}],
 
57
            "dev_type": "partition"},
 
58
           {"device": "/sys/class/block/vdb/vdb7", 'name': 'vdb7', "holders":
 
59
            [{"device": "/sys/class/block/bcache2", 'name': 'bcache2',
 
60
              "holders": [], "dev_type": "bcache"}],
 
61
            "dev_type": "partition"},
 
62
           {"device": "/sys/class/block/vdb/vdb8", 'name': 'vdb8',
 
63
            "holders": [], "dev_type": "partition"}],
 
64
          "dev_type": "disk"},
 
65
         {"device": "/sys/class/block/vdc", 'name': 'vdc', "holders": [],
 
66
          "dev_type": "disk"},
 
67
         {"device": "/sys/class/block/vdd", 'name': 'vdd', "holders":
 
68
          [{"device": "/sys/class/block/vdd/vdd1", 'name': 'vdd1',
 
69
            "holders": [], "dev_type": "partition"}],
 
70
          "dev_type": "disk"}],
 
71
    ]
 
72
 
 
73
    @mock.patch('curtin.block.clear_holders.block')
 
74
    @mock.patch('curtin.block.clear_holders.util')
 
75
    def test_get_dmsetup_uuid(self, mock_util, mock_block):
 
76
        """ensure that clear_holders.get_dmsetup_uuid works as expected"""
 
77
        uuid = "CRYPT-LUKS1-fe335a74374e4649af9776c1699676f8-sdb5_crypt"
 
78
        mock_block.sysfs_to_devpath.return_value = self.test_blockdev
 
79
        mock_util.subp.return_value = (' ' + uuid + '\n', None)
 
80
        res = clear_holders.get_dmsetup_uuid(self.test_syspath)
 
81
        mock_util.subp.assert_called_with(
 
82
            ['dmsetup', 'info', self.test_blockdev, '-C', '-o',
 
83
             'uuid', '--noheadings'], capture=True)
 
84
        self.assertEqual(res, uuid)
 
85
        mock_block.sysfs_to_devpath.assert_called_with(self.test_syspath)
 
86
 
 
87
    @mock.patch('curtin.block.clear_holders.block')
 
88
    @mock.patch('curtin.block.clear_holders.os')
 
89
    def test_get_bcache_using_dev(self, mock_os, mock_block):
 
90
        """Ensure that get_bcache_using_dev works"""
 
91
        fake_bcache = '/sys/fs/bcache/fake'
 
92
        mock_os.path.realpath.return_value = fake_bcache
 
93
        mock_os.path.exists.side_effect = lambda x: x == fake_bcache
 
94
        mock_block.sys_block_path.return_value = self.test_blockdev
 
95
        bcache_dir = clear_holders.get_bcache_using_dev(self.test_blockdev)
 
96
        self.assertEqual(bcache_dir, fake_bcache)
 
97
 
 
98
    @mock.patch('curtin.block.clear_holders.get_dmsetup_uuid')
 
99
    @mock.patch('curtin.block.clear_holders.block')
 
100
    def test_differentiate_lvm_and_crypt(
 
101
            self, mock_block, mock_get_dmsetup_uuid):
 
102
        """test clear_holders.identify_lvm and clear_holders.identify_crypt"""
 
103
        for (kname, dm_uuid, is_lvm, is_crypt) in [
 
104
                ('dm-0', 'LVM-abcdefg', True, False),
 
105
                ('sda', 'LVM-abcdefg', False, False),
 
106
                ('sda', 'CRYPT-abcdefg', False, False),
 
107
                ('dm-0', 'CRYPT-abcdefg', False, True),
 
108
                ('dm-1', 'invalid', False, False)]:
 
109
            mock_block.path_to_kname.return_value = kname
 
110
            mock_get_dmsetup_uuid.return_value = dm_uuid
 
111
            self.assertEqual(
 
112
                is_lvm, clear_holders.identify_lvm(self.test_syspath))
 
113
            self.assertEqual(
 
114
                is_crypt, clear_holders.identify_crypt(self.test_syspath))
 
115
            mock_block.path_to_kname.assert_called_with(self.test_syspath)
 
116
            mock_get_dmsetup_uuid.assert_called_with(self.test_syspath)
 
117
 
 
118
    @mock.patch('curtin.block.clear_holders.util')
 
119
    @mock.patch('curtin.block.clear_holders.os')
 
120
    @mock.patch('curtin.block.clear_holders.LOG')
 
121
    @mock.patch('curtin.block.clear_holders.get_bcache_using_dev')
 
122
    def test_shutdown_bcache(self, mock_get_bcache, mock_log, mock_os,
 
123
                             mock_util):
 
124
        """test clear_holders.shutdown_bcache"""
 
125
        mock_os.path.exists.return_value = True
 
126
        mock_os.path.join.side_effect = os.path.join
 
127
        mock_get_bcache.return_value = self.test_blockdev
 
128
        clear_holders.shutdown_bcache(self.test_syspath)
 
129
        mock_get_bcache.assert_called_with(self.test_syspath)
 
130
        self.assertTrue(mock_log.debug.called)
 
131
        self.assertFalse(mock_log.warn.called)
 
132
        mock_util.write_file.assert_called_with(self.test_blockdev + '/stop',
 
133
                                                '1', mode=None)
 
134
 
 
135
    @mock.patch('curtin.block.clear_holders.LOG')
 
136
    @mock.patch('curtin.block.clear_holders.block.sys_block_path')
 
137
    @mock.patch('curtin.block.clear_holders.lvm')
 
138
    @mock.patch('curtin.block.clear_holders.util')
 
139
    def test_shutdown_lvm(self, mock_util, mock_lvm, mock_syspath, mock_log):
 
140
        """test clear_holders.shutdown_lvm"""
 
141
        vg_name = 'volgroup1'
 
142
        lv_name = 'lvol1'
 
143
        mock_syspath.return_value = self.test_blockdev
 
144
        mock_util.load_file.return_value = '-'.join((vg_name, lv_name))
 
145
        mock_lvm.split_lvm_name.return_value = (vg_name, lv_name)
 
146
        mock_lvm.get_lvols_in_volgroup.return_value = ['lvol2']
 
147
        clear_holders.shutdown_lvm(self.test_blockdev)
 
148
        mock_syspath.assert_called_with(self.test_blockdev)
 
149
        mock_util.load_file.assert_called_with(self.test_blockdev + '/dm/name')
 
150
        mock_lvm.split_lvm_name.assert_called_with(
 
151
            '-'.join((vg_name, lv_name)))
 
152
        self.assertTrue(mock_log.debug.called)
 
153
        mock_util.subp.assert_called_with(
 
154
            ['lvremove', '--force', '--force', '/'.join((vg_name, lv_name))],
 
155
            rcs=[0, 5])
 
156
        mock_lvm.get_lvols_in_volgroup.assert_called_with(vg_name)
 
157
        self.assertEqual(len(mock_util.subp.call_args_list), 1)
 
158
        self.assertTrue(mock_lvm.lvm_scan.called)
 
159
        mock_lvm.get_lvols_in_volgroup.return_value = []
 
160
        clear_holders.shutdown_lvm(self.test_blockdev)
 
161
        mock_util.subp.assert_called_with(
 
162
            ['vgremove', '--force', '--force', vg_name], rcs=[0, 5])
 
163
 
 
164
    @mock.patch('curtin.block.clear_holders.block')
 
165
    @mock.patch('curtin.block.clear_holders.util')
 
166
    def test_shutdown_crypt(self, mock_util, mock_block):
 
167
        """test clear_holders.shutdown_crypt"""
 
168
        mock_block.sysfs_to_devpath.return_value = self.test_blockdev
 
169
        clear_holders.shutdown_crypt(self.test_syspath)
 
170
        mock_block.sysfs_to_devpath.assert_called_with(self.test_syspath)
 
171
        mock_util.subp.assert_called_with(
 
172
            ['cryptsetup', 'remove', self.test_blockdev], capture=True)
 
173
 
 
174
    @mock.patch('curtin.block.clear_holders.LOG')
 
175
    @mock.patch('curtin.block.clear_holders.block')
 
176
    def test_shutdown_mdadm(self, mock_block, mock_log):
 
177
        """test clear_holders.shutdown_mdadm"""
 
178
        mock_block.sysfs_to_devpath.return_value = self.test_blockdev
 
179
        clear_holders.shutdown_mdadm(self.test_syspath)
 
180
        mock_block.mdadm.mdadm_stop.assert_called_with(self.test_blockdev)
 
181
        mock_block.mdadm.mdadm_remove.assert_called_with(self.test_blockdev)
 
182
        self.assertTrue(mock_log.debug.called)
 
183
 
 
184
    @mock.patch('curtin.block.clear_holders.LOG')
 
185
    @mock.patch('curtin.block.clear_holders.block')
 
186
    def test_clear_holders_wipe_superblock(self, mock_block, mock_log):
 
187
        """test clear_holders.wipe_superblock handles errors right"""
 
188
        mock_block.sysfs_to_devpath.return_value = self.test_blockdev
 
189
        mock_block.is_extended_partition.return_value = True
 
190
        clear_holders.wipe_superblock(self.test_syspath)
 
191
        self.assertFalse(mock_block.wipe_volume.called)
 
192
        mock_block.is_extended_partition.return_value = False
 
193
        clear_holders.wipe_superblock(self.test_syspath)
 
194
        mock_block.sysfs_to_devpath.assert_called_with(self.test_syspath)
 
195
        mock_block.wipe_volume.assert_called_with(
 
196
            self.test_blockdev, mode='superblock')
 
197
 
 
198
    @mock.patch('curtin.block.clear_holders.LOG')
 
199
    @mock.patch('curtin.block.clear_holders.block')
 
200
    @mock.patch('curtin.block.clear_holders.os')
 
201
    def test_get_holders(self, mock_os, mock_block, mock_log):
 
202
        """test clear_holders.get_holders"""
 
203
        mock_block.sys_block_path.return_value = self.test_syspath
 
204
        mock_os.path.join.side_effect = os.path.join
 
205
        clear_holders.get_holders(self.test_blockdev)
 
206
        mock_block.sys_block_path.assert_called_with(self.test_blockdev)
 
207
        mock_os.path.join.assert_called_with(self.test_syspath, 'holders')
 
208
        self.assertTrue(mock_log.debug.called)
 
209
        mock_os.listdir.assert_called_with(
 
210
            os.path.join(self.test_syspath, 'holders'))
 
211
 
 
212
    def test_plan_shutdown_holders_trees(self):
 
213
        """
 
214
        make sure clear_holdrs.plan_shutdown_holders_tree orders shutdown
 
215
        functions correctly and uses the appropriate shutdown function for each
 
216
        dev type
 
217
        """
 
218
        # trees that have been generated, checked for correctness,
 
219
        # and the order that they should be shut down in (by level)
 
220
        test_trees_and_orders = [
 
221
            (self.example_holders_trees[0][0],
 
222
             ({'dm-3'}, {'dm-1', 'dm-2'}, {'dm-0'}, {'sda5', 'sda2', 'sda1'},
 
223
              {'sda'})),
 
224
            (self.example_holders_trees[1],
 
225
             ({'bcache1'}, {'bcache2', 'md0'},
 
226
              {'vdb1', 'vdb2', 'vdb3', 'vdb4', 'vdb5', 'vdb6', 'vdb7', 'vdb8',
 
227
               'vdd1'},
 
228
              {'vdb', 'vdc', 'vdd'}))
 
229
        ]
 
230
        for tree, correct_order in test_trees_and_orders:
 
231
            res = clear_holders.plan_shutdown_holder_trees(tree)
 
232
            for level in correct_order:
 
233
                self.assertEqual({os.path.basename(e['device'])
 
234
                                  for e in res[:len(level)]}, level)
 
235
                res = res[len(level):]
 
236
 
 
237
    def test_format_holders_tree(self):
 
238
        """test output of clear_holders.format_holders_tree"""
 
239
        test_trees_and_results = [
 
240
            (self.example_holders_trees[0][0],
 
241
             textwrap.dedent("""
 
242
                 sda
 
243
                 |-- sda1
 
244
                 |-- sda2
 
245
                 `-- sda5
 
246
                     `-- dm-0
 
247
                         |-- dm-1
 
248
                         `-- dm-2
 
249
                             `-- dm-3
 
250
                 """).strip()),
 
251
            (self.example_holders_trees[1][0],
 
252
             textwrap.dedent("""
 
253
                 vdb
 
254
                 |-- vdb1
 
255
                 |-- vdb2
 
256
                 |-- vdb3
 
257
                 |   `-- md0
 
258
                 |       `-- bcache1
 
259
                 |-- vdb4
 
260
                 |   `-- md0
 
261
                 |       `-- bcache1
 
262
                 |-- vdb5
 
263
                 |   `-- md0
 
264
                 |       `-- bcache1
 
265
                 |-- vdb6
 
266
                 |   |-- bcache1
 
267
                 |   `-- bcache2
 
268
                 |-- vdb7
 
269
                 |   `-- bcache2
 
270
                 `-- vdb8
 
271
                 """).strip()),
 
272
            (self.example_holders_trees[1][1], 'vdc'),
 
273
            (self.example_holders_trees[1][2],
 
274
             textwrap.dedent("""
 
275
                 vdd
 
276
                 `-- vdd1
 
277
                 """).strip())
 
278
        ]
 
279
        for tree, result in test_trees_and_results:
 
280
            self.assertEqual(clear_holders.format_holders_tree(tree), result)
 
281
 
 
282
    def test_get_holder_types(self):
 
283
        """test clear_holders.get_holder_types"""
 
284
        test_trees_and_results = [
 
285
            (self.example_holders_trees[0][0],
 
286
             {('disk', '/sys/class/block/sda'),
 
287
              ('partition', '/sys/class/block/sda/sda1'),
 
288
              ('partition', '/sys/class/block/sda/sda2'),
 
289
              ('partition', '/sys/class/block/sda/sda5'),
 
290
              ('crypt', '/sys/class/block/dm-0'),
 
291
              ('lvm', '/sys/class/block/dm-1'),
 
292
              ('lvm', '/sys/class/block/dm-2'),
 
293
              ('crypt', '/sys/class/block/dm-3')}),
 
294
            (self.example_holders_trees[1][0],
 
295
             {('disk', '/sys/class/block/vdb'),
 
296
              ('partition', '/sys/class/block/vdb/vdb1'),
 
297
              ('partition', '/sys/class/block/vdb/vdb2'),
 
298
              ('partition', '/sys/class/block/vdb/vdb3'),
 
299
              ('partition', '/sys/class/block/vdb/vdb4'),
 
300
              ('partition', '/sys/class/block/vdb/vdb5'),
 
301
              ('partition', '/sys/class/block/vdb/vdb6'),
 
302
              ('partition', '/sys/class/block/vdb/vdb7'),
 
303
              ('partition', '/sys/class/block/vdb/vdb8'),
 
304
              ('raid', '/sys/class/block/md0'),
 
305
              ('bcache', '/sys/class/block/bcache1'),
 
306
              ('bcache', '/sys/class/block/bcache2')})
 
307
        ]
 
308
        for tree, result in test_trees_and_results:
 
309
            self.assertEqual(clear_holders.get_holder_types(tree), result)
 
310
 
 
311
    @mock.patch('curtin.block.clear_holders.block.sys_block_path')
 
312
    @mock.patch('curtin.block.clear_holders.gen_holders_tree')
 
313
    def test_assert_clear(self, mock_gen_holders_tree, mock_syspath):
 
314
        mock_gen_holders_tree.return_value = self.example_holders_trees[0][0]
 
315
        mock_syspath.side_effect = lambda x: x
 
316
        device = '/dev/null'
 
317
        with self.assertRaises(OSError):
 
318
            clear_holders.assert_clear(device)
 
319
            mock_gen_holders_tree.assert_called_with(device)
 
320
        mock_gen_holders_tree.return_value = self.example_holders_trees[1][1]
 
321
        clear_holders.assert_clear(device)
 
322
 
 
323
    @mock.patch('curtin.block.clear_holders.block.mdadm')
 
324
    @mock.patch('curtin.block.clear_holders.util')
 
325
    def test_start_clear_holders_deps(self, mock_util, mock_mdadm):
 
326
        clear_holders.start_clear_holders_deps()
 
327
        mock_mdadm.mdadm_assemble.assert_called_with(
 
328
            scan=True, ignore_errors=True)
 
329
        mock_util.subp.assert_called_with(['modprobe', 'bcache'], rcs=[0, 1])