Browse Source

Wire in file image support

Change-Id: If729c8cde28186dc28ec994ddd23c981c9313fe2
Story: #2002048
Task: #26380
Dmitry Tantsur 7 months ago
parent
commit
98ad1d86cd
4 changed files with 129 additions and 3 deletions
  1. 21
    2
      metalsmith/_cmd.py
  2. 1
    1
      metalsmith/sources.py
  3. 48
    0
      metalsmith/test/test_cmd.py
  4. 59
    0
      metalsmith/test/test_provisioner.py

+ 21
- 2
metalsmith/_cmd.py View File

@@ -73,6 +73,23 @@ def _do_deploy(api, args, formatter):
73 73
                                                 **kwargs)
74 74
         else:
75 75
             source = sources.HttpWholeDiskImage(args.image, **kwargs)
76
+    elif args.image.startswith('file://'):
77
+        if not args.image_checksum:
78
+            raise RuntimeError("File images require --image-checksum")
79
+
80
+        if args.image_kernel or args.image_ramdisk:
81
+            if not (args.image_kernel.startswith('file://') and
82
+                    args.image_ramdisk.startswith('file://')):
83
+                raise RuntimeError('Images with the file:// schema require '
84
+                                   'kernel and ramdisk images to also use '
85
+                                   'the file:// schema')
86
+            source = sources.FilePartitionImage(args.image,
87
+                                                args.image_kernel,
88
+                                                args.image_ramdisk,
89
+                                                args.image_checksum)
90
+        else:
91
+            source = sources.FileWholeDiskImage(args.image,
92
+                                                args.image_checksum)
76 93
     else:
77 94
         source = args.image
78 95
 
@@ -152,8 +169,10 @@ def _parse_args(args, config):
152 169
                         required=True)
153 170
     deploy.add_argument('--image-checksum',
154 171
                         help='image MD5 checksum or URL with checksums')
155
-    deploy.add_argument('--image-kernel', help='URL of the image\'s kernel')
156
-    deploy.add_argument('--image-ramdisk', help='URL of the image\'s ramdisk')
172
+    deploy.add_argument('--image-kernel', help='URL of the image\'s kernel',
173
+                        default='')
174
+    deploy.add_argument('--image-ramdisk', help='URL of the image\'s ramdisk',
175
+                        default='')
157 176
     deploy.add_argument('--network', help='network to use (name or UUID)',
158 177
                         dest='nics', action=NICAction)
159 178
     deploy.add_argument('--port', help='port to attach (name or UUID)',

+ 1
- 1
metalsmith/sources.py View File

@@ -208,7 +208,7 @@ class FileWholeDiskImage(_Source):
208 208
         }
209 209
 
210 210
 
211
-class FilePartitionImage(_Source):
211
+class FilePartitionImage(FileWholeDiskImage):
212 212
     """A partition image from a local file location.
213 213
 
214 214
     .. warning::

+ 48
- 0
metalsmith/test/test_cmd.py View File

@@ -393,6 +393,54 @@ class TestDeploy(testtools.TestCase):
393 393
         self.assertEqual('https://example.com/ramdisk', source.ramdisk_url)
394 394
         self.assertEqual('95e750180c7921ea0d545c7165db66b8', source.checksum)
395 395
 
396
+    def test_args_file_whole_disk_image(self, mock_pr):
397
+        args = ['deploy', '--image', 'file:///var/lib/ironic/image.img',
398
+                '--image-checksum', '95e750180c7921ea0d545c7165db66b8',
399
+                '--network', 'mynet', '--resource-class', 'compute']
400
+        self._check(mock_pr, args, {}, {'image': mock.ANY})
401
+
402
+        source = mock_pr.return_value.provision_node.call_args[1]['image']
403
+        self.assertIsInstance(source, sources.FileWholeDiskImage)
404
+        self.assertEqual('file:///var/lib/ironic/image.img', source.location)
405
+        self.assertEqual('95e750180c7921ea0d545c7165db66b8', source.checksum)
406
+
407
+    def test_args_file_partition_disk_image(self, mock_pr):
408
+        args = ['deploy', '--image', 'file:///var/lib/ironic/image.img',
409
+                '--image-kernel', 'file:///var/lib/ironic/image.vmlinuz',
410
+                '--image-ramdisk', 'file:///var/lib/ironic/image.initrd',
411
+                '--image-checksum', '95e750180c7921ea0d545c7165db66b8',
412
+                '--network', 'mynet', '--resource-class', 'compute']
413
+        self._check(mock_pr, args, {}, {'image': mock.ANY})
414
+
415
+        source = mock_pr.return_value.provision_node.call_args[1]['image']
416
+        self.assertIsInstance(source, sources.FilePartitionImage)
417
+        self.assertEqual('file:///var/lib/ironic/image.img', source.location)
418
+        self.assertEqual('file:///var/lib/ironic/image.vmlinuz',
419
+                         source.kernel_location)
420
+        self.assertEqual('file:///var/lib/ironic/image.initrd',
421
+                         source.ramdisk_location)
422
+        self.assertEqual('95e750180c7921ea0d545c7165db66b8', source.checksum)
423
+
424
+    @mock.patch.object(_cmd.LOG, 'critical', autospec=True)
425
+    def test_args_file_image_without_checksum(self, mock_log, mock_pr):
426
+        args = ['deploy', '--image', 'file:///var/lib/ironic/image.img',
427
+                '--resource-class', 'compute']
428
+        self.assertRaises(SystemExit, _cmd.main, args)
429
+        self.assertTrue(mock_log.called)
430
+        self.assertFalse(mock_pr.return_value.reserve_node.called)
431
+        self.assertFalse(mock_pr.return_value.provision_node.called)
432
+
433
+    @mock.patch.object(_cmd.LOG, 'critical', autospec=True)
434
+    def test_args_file_image_with_incorrect_kernel(self, mock_log, mock_pr):
435
+        args = ['deploy', '--image', 'file:///var/lib/ironic/image.img',
436
+                '--image-kernel', 'http://example.com/image.vmlinuz',
437
+                '--image-checksum', '95e750180c7921ea0d545c7165db66b8',
438
+                '--resource-class', 'compute']
439
+        self.assertRaises(SystemExit, _cmd.main, args)
440
+        self.assertTrue(mock_log.called)
441
+        self.assertFalse(mock_pr.return_value.reserve_node.called)
442
+        self.assertFalse(mock_pr.return_value.provision_node.called)
443
+
396 444
     def test_args_custom_wait(self, mock_pr):
397 445
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
398 446
                 '--wait', '3600', '--resource-class', 'compute']

+ 59
- 0
metalsmith/test/test_provisioner.py View File

@@ -585,6 +585,65 @@ abcd  image
585 585
         self.assertFalse(self.api.release_node.called)
586 586
         self.assertFalse(self.conn.network.delete_port.called)
587 587
 
588
+    def test_with_file_whole_disk(self):
589
+        self.updates['/instance_info/image_source'] = 'file:///foo/img'
590
+        self.updates['/instance_info/image_checksum'] = 'abcd'
591
+        del self.updates['/instance_info/kernel']
592
+        del self.updates['/instance_info/ramdisk']
593
+
594
+        inst = self.pr.provision_node(
595
+            self.node,
596
+            sources.FileWholeDiskImage('file:///foo/img', checksum='abcd'),
597
+            [{'network': 'network'}])
598
+
599
+        self.assertEqual(inst.uuid, self.node.uuid)
600
+        self.assertEqual(inst.node, self.node)
601
+
602
+        self.assertFalse(self.conn.image.find_image.called)
603
+        self.conn.network.create_port.assert_called_once_with(
604
+            network_id=self.conn.network.find_network.return_value.id)
605
+        self.api.attach_port_to_node.assert_called_once_with(
606
+            self.node.uuid, self.conn.network.create_port.return_value.id)
607
+        self.api.update_node.assert_called_once_with(self.node, self.updates)
608
+        self.api.validate_node.assert_called_once_with(self.node,
609
+                                                       validate_deploy=True)
610
+        self.api.node_action.assert_called_once_with(self.node, 'active',
611
+                                                     configdrive=mock.ANY)
612
+        self.assertFalse(self.wait_mock.called)
613
+        self.assertFalse(self.api.release_node.called)
614
+        self.assertFalse(self.conn.network.delete_port.called)
615
+
616
+    def test_with_file_partition(self):
617
+        self.updates['/instance_info/image_source'] = 'file:///foo/img'
618
+        self.updates['/instance_info/image_checksum'] = 'abcd'
619
+        self.updates['/instance_info/kernel'] = 'file:///foo/vmlinuz'
620
+        self.updates['/instance_info/ramdisk'] = 'file:///foo/initrd'
621
+
622
+        inst = self.pr.provision_node(
623
+            self.node,
624
+            sources.FilePartitionImage('/foo/img',
625
+                                       '/foo/vmlinuz',
626
+                                       '/foo/initrd',
627
+                                       checksum='abcd'),
628
+            [{'network': 'network'}])
629
+
630
+        self.assertEqual(inst.uuid, self.node.uuid)
631
+        self.assertEqual(inst.node, self.node)
632
+
633
+        self.assertFalse(self.conn.image.find_image.called)
634
+        self.conn.network.create_port.assert_called_once_with(
635
+            network_id=self.conn.network.find_network.return_value.id)
636
+        self.api.attach_port_to_node.assert_called_once_with(
637
+            self.node.uuid, self.conn.network.create_port.return_value.id)
638
+        self.api.update_node.assert_called_once_with(self.node, self.updates)
639
+        self.api.validate_node.assert_called_once_with(self.node,
640
+                                                       validate_deploy=True)
641
+        self.api.node_action.assert_called_once_with(self.node, 'active',
642
+                                                     configdrive=mock.ANY)
643
+        self.assertFalse(self.wait_mock.called)
644
+        self.assertFalse(self.api.release_node.called)
645
+        self.assertFalse(self.conn.network.delete_port.called)
646
+
588 647
     def test_with_root_size(self):
589 648
         self.updates['/instance_info/root_gb'] = 50
590 649
 

Loading…
Cancel
Save