Browse Source

Support partition HTTP images in CLI

Also removed some dead code from sources.

Change-Id: I570eda45285771068711ef90d22550632411e98f
Story: #2002048
Task: #26208
Dmitry Tantsur 7 months ago
parent
commit
fc85cb9230

+ 13
- 4
metalsmith/_cmd.py View File

@@ -58,14 +58,21 @@ def _do_deploy(api, args, formatter):
58 58
         raise RuntimeError("%s cannot be used as a hostname" % args.hostname)
59 59
 
60 60
     if _is_http(args.image):
61
+        kwargs = {}
61 62
         if not args.image_checksum:
62 63
             raise RuntimeError("HTTP(s) images require --image-checksum")
63 64
         elif _is_http(args.image_checksum):
64
-            source = sources.HttpWholeDiskImage(
65
-                args.image, checksum_url=args.image_checksum)
65
+            kwargs['checksum_url'] = args.image_checksum
66 66
         else:
67
-            source = sources.HttpWholeDiskImage(
68
-                args.image, checksum=args.image_checksum)
67
+            kwargs['checksum'] = args.image_checksum
68
+
69
+        if args.image_kernel or args.image_ramdisk:
70
+            source = sources.HttpPartitionImage(args.image,
71
+                                                args.image_kernel,
72
+                                                args.image_ramdisk,
73
+                                                **kwargs)
74
+        else:
75
+            source = sources.HttpWholeDiskImage(args.image, **kwargs)
69 76
     else:
70 77
         source = args.image
71 78
 
@@ -145,6 +152,8 @@ def _parse_args(args, config):
145 152
                         required=True)
146 153
     deploy.add_argument('--image-checksum',
147 154
                         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')
148 157
     deploy.add_argument('--network', help='network to use (name or UUID)',
149 158
                         dest='nics', action=NICAction)
150 159
     deploy.add_argument('--port', help='port to attach (name or UUID)',

+ 68
- 4
metalsmith/sources.py View File

@@ -90,8 +90,7 @@ class HttpWholeDiskImage(_Source):
90 90
     specifically, by **ironic-conductor** processes).
91 91
     """
92 92
 
93
-    def __init__(self, url, checksum=None, checksum_url=None,
94
-                 kernel_url=None, ramdisk_url=None):
93
+    def __init__(self, url, checksum=None, checksum_url=None):
95 94
         """Create an HTTP source.
96 95
 
97 96
         :param url: URL of the image.
@@ -108,8 +107,6 @@ class HttpWholeDiskImage(_Source):
108 107
         self.url = url
109 108
         self.checksum = checksum
110 109
         self.checksum_url = checksum_url
111
-        self.kernel_url = kernel_url
112
-        self.ramdisk_url = ramdisk_url
113 110
 
114 111
     def _validate(self, connection):
115 112
         # TODO(dtantsur): should we validate image URLs here? Ironic will do it
@@ -178,3 +175,70 @@ class HttpPartitionImage(HttpWholeDiskImage):
178 175
         updates['/instance_info/kernel'] = self.kernel_url
179 176
         updates['/instance_info/ramdisk'] = self.ramdisk_url
180 177
         return updates
178
+
179
+
180
+class FileWholeDiskImage(_Source):
181
+    """A whole-disk image from a local file location.
182
+
183
+    .. warning::
184
+        The location must be local to the **ironic-conductor** process handling
185
+        the node, not to metalsmith itself! Since there is no easy way to
186
+        determine which conductor handles a node, the same file must be
187
+        available at the same location to all conductors in the same group.
188
+    """
189
+
190
+    def __init__(self, location, checksum):
191
+        """Create a local file source.
192
+
193
+        :param location: Location of the image, optionally starting with
194
+            ``file://``.
195
+        :param checksum: MD5 checksum of the image.
196
+        """
197
+        if not location.startswith('file://'):
198
+            location = 'file://' + location
199
+        self.location = location
200
+        self.checksum = checksum
201
+
202
+    def _node_updates(self, connection):
203
+        LOG.debug('Image: %(image)s, checksum %(checksum)s',
204
+                  {'image': self.location, 'checksum': self.checksum})
205
+        return {
206
+            '/instance_info/image_source': self.location,
207
+            '/instance_info/image_checksum': self.checksum,
208
+        }
209
+
210
+
211
+class FilePartitionImage(_Source):
212
+    """A partition image from a local file location.
213
+
214
+    .. warning::
215
+        The location must be local to the **ironic-conductor** process handling
216
+        the node, not to metalsmith itself! Since there is no easy way to
217
+        determine which conductor handles a node, the same file must be
218
+        available at the same location to all conductors in the same group.
219
+    """
220
+
221
+    def __init__(self, location, kernel_location, ramdisk_location, checksum):
222
+        """Create a local file source.
223
+
224
+        :param location: Location of the image, optionally starting with
225
+            ``file://``.
226
+        :param kernel_location: Location of the kernel of the image,
227
+            optionally starting with ``file://``.
228
+        :param ramdisk_location: Location of the ramdisk of the image,
229
+            optionally starting with ``file://``.
230
+        :param checksum: MD5 checksum of the image.
231
+        """
232
+        super(FilePartitionImage, self).__init__(location, checksum)
233
+        if not kernel_location.startswith('file://'):
234
+            kernel_location = 'file://' + kernel_location
235
+        if not ramdisk_location.startswith('file://'):
236
+            ramdisk_location = 'file://' + ramdisk_location
237
+        self.kernel_location = kernel_location
238
+        self.ramdisk_location = ramdisk_location
239
+
240
+    def _node_updates(self, connection):
241
+        updates = super(FilePartitionImage, self)._node_updates(connection)
242
+        updates['/instance_info/kernel'] = self.kernel_location
243
+        updates['/instance_info/ramdisk'] = self.ramdisk_location
244
+        return updates

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

@@ -378,6 +378,21 @@ class TestDeploy(testtools.TestCase):
378 378
         self.assertFalse(mock_pr.return_value.reserve_node.called)
379 379
         self.assertFalse(mock_pr.return_value.provision_node.called)
380 380
 
381
+    def test_args_http_partition_image(self, mock_pr):
382
+        args = ['deploy', '--image', 'https://example.com/image.img',
383
+                '--image-kernel', 'https://example.com/kernel',
384
+                '--image-ramdisk', 'https://example.com/ramdisk',
385
+                '--image-checksum', '95e750180c7921ea0d545c7165db66b8',
386
+                '--network', 'mynet', '--resource-class', 'compute']
387
+        self._check(mock_pr, args, {}, {'image': mock.ANY})
388
+
389
+        source = mock_pr.return_value.provision_node.call_args[1]['image']
390
+        self.assertIsInstance(source, sources.HttpPartitionImage)
391
+        self.assertEqual('https://example.com/image.img', source.url)
392
+        self.assertEqual('https://example.com/kernel', source.kernel_url)
393
+        self.assertEqual('https://example.com/ramdisk', source.ramdisk_url)
394
+        self.assertEqual('95e750180c7921ea0d545c7165db66b8', source.checksum)
395
+
381 396
     def test_args_custom_wait(self, mock_pr):
382 397
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
383 398
                 '--wait', '3600', '--resource-class', 'compute']

+ 27
- 0
playbooks/integration/cirros-image.yaml View File

@@ -24,6 +24,29 @@
24 24
       register: baremetal_endpoint_result
25 25
       failed_when: baremetal_endpoint_result.stdout == ""
26 26
 
27
+    - name: Copy partition images directory
28
+      command: >
29
+        cp -r /opt/stack/devstack/files/images/{{ cirros_uec_image_result.stdout }}
30
+            /opt/stack/data/ironic/httpboot/metalsmith
31
+      args:
32
+        creates: /opt/stack/data/ironic/httpboot/metalsmith
33
+      become: yes
34
+
35
+    - name: Create MD5 checksums file for partition images
36
+      shell: md5sum cirros-* > CHECKSUMS
37
+      args:
38
+        chdir: /opt/stack/data/ironic/httpboot/metalsmith
39
+      become: yes
40
+
41
+    - name: Change ownership of image files
42
+      file:
43
+        path: /opt/stack/data/ironic/httpboot/metalsmith
44
+        state: directory
45
+        owner: "{{ ansible_user }}"
46
+        recurse: yes
47
+        mode: a+r
48
+      become: yes
49
+
27 50
     - name: Calculate MD5 checksum for HTTP disk image
28 51
       shell: |
29 52
           md5sum /opt/stack/devstack/files/{{ cirros_disk_image_result.stdout }}.img \
@@ -33,6 +56,10 @@
33 56
 
34 57
     - name: Set facts for HTTP image
35 58
       set_fact:
59
+        metalsmith_partition_image: "{{ baremetal_endpoint_result.stdout}}/metalsmith/{{ cirros_uec_image_result.stdout | replace('-uec', '-blank') }}.img"
60
+        metalsmith_partition_kernel_image: "{{ baremetal_endpoint_result.stdout}}/metalsmith/{{ cirros_uec_image_result.stdout | replace('-uec', '-vmlinuz') }}"
61
+        metalsmith_partition_ramdisk_image: "{{ baremetal_endpoint_result.stdout}}/metalsmith/{{ cirros_uec_image_result.stdout | replace('-uec', '-initrd') }}"
62
+        metalsmith_partition_checksum: "{{ baremetal_endpoint_result.stdout}}/metalsmith/CHECKSUMS"
36 63
         metalsmith_whole_disk_image: "{{ baremetal_endpoint_result.stdout}}/{{ cirros_disk_image_result.stdout }}.img"
37 64
         metalsmith_whole_disk_checksum: "{{ cirros_disk_image_checksum_result.stdout }}"
38 65
 

+ 0
- 2
playbooks/integration/exercise.yaml View File

@@ -22,8 +22,6 @@
22 22
     metalsmith_resource_class: baremetal
23 23
     metalsmith_instances:
24 24
       - hostname: test
25
-        image: "{{ image }}"
26
-        image_checksum: "{{ image_checksum | default('') }}"
27 25
         nics:
28 26
           - "{{ nic }}"
29 27
         ssh_public_keys:

+ 6
- 6
playbooks/integration/run.yaml View File

@@ -11,15 +11,15 @@
11 11
     - name: Test a whole-disk image
12 12
       include: exercise.yaml
13 13
       vars:
14
-        image: "{{ metalsmith_whole_disk_image }}"
15
-        image_checksum: "{{ metalsmith_whole_disk_checksum | default('') }}"
14
+        metalsmith_image: "{{ metalsmith_whole_disk_image }}"
15
+        metalsmith_image_checksum: "{{ metalsmith_whole_disk_checksum | default('') }}"
16 16
         # NOTE(dtantsur): cannot specify swap with whole disk images
17 17
         metalsmith_swap_size:
18 18
 
19 19
     - name: Test a partition image
20 20
       include: exercise.yaml
21 21
       vars:
22
-        image: "{{ metalsmith_partition_image }}"
23
-        image_checksum: "{{ metalsmith_partition_checksum | default('') }}"
24
-      # FIXME(dtantsur): cover partition images
25
-      when: not (metalsmith_use_http | default(false))
22
+        metalsmith_image: "{{ metalsmith_partition_image }}"
23
+        metalsmith_image_checksum: "{{ metalsmith_partition_checksum | default('') }}"
24
+        metalsmith_image_kernel: "{{ metalsmith_partition_kernel_image | default('') }}"
25
+        metalsmith_image_ramdisk: "{{ metalsmith_partition_ramdisk_image | default('') }}"

+ 20
- 0
roles/metalsmith_deployment/README.rst View File

@@ -25,6 +25,10 @@ The following optional variables provide the defaults for Instance_ attributes:
25 25
     the default for ``image``.
26 26
 ``metalsmith_image_checksum``
27 27
     the default for ``image_checksum``.
28
+``metalsmith_image_kernel``
29
+    the default for ``image_kernel``.
30
+``metalsmith_image_ramdisk``
31
+    the default for ``image_ramdisk``.
28 32
 ``metalsmith_netboot``
29 33
     the default for ``netboot``
30 34
 ``metalsmith_nics``
@@ -62,6 +66,12 @@ Each instances has the following attributes:
62 66
     UUID, name or HTTP(s) URL of the image to use for deployment. Mandatory.
63 67
 ``image_checksum`` (defaults to ``metalsmith_image_checksum``)
64 68
     MD5 checksum or checksum file URL for an HTTP(s) image.
69
+``image_kernel`` (defaults to ``metalsmith_image_kernel``)
70
+    URL of the kernel image if and only if the ``image`` is a URL of
71
+    a partition image.
72
+``image_ramdisk`` (defaults to ``metalsmith_image_ramdisk``)
73
+    URL of the ramdisk image if and only if the ``image`` is a URL of
74
+    a partition image.
65 75
 ``netboot``
66 76
     whether to boot the deployed instance from network (PXE, iPXE, etc).
67 77
     The default is to use local boot (requires a bootloader on the image).
@@ -152,3 +162,13 @@ Example
152 162
                 nics:
153 163
                   - network: ctlplane
154 164
                   - port: 1899af15-149d-47dc-b0dc-a68614eeb5c4
165
+              - hostname: custom-partition-image
166
+                resource_class: custom
167
+                image: https://example.com/images/custom-1.0.root.img
168
+                image_kernel: https://example.com/images/custom-1.0.vmlinuz
169
+                image_ramdisk: https://example.com/images/custom-1.0.initrd
170
+                image_checksum: https://example.com/images/MD5SUMS
171
+              - hostname: custom-whole-disk-image
172
+                resource_class: custom
173
+                image: https://example.com/images/custom-1.0.qcow2
174
+                image_checksum: https://example.com/images/MD5SUMS

+ 2
- 0
roles/metalsmith_deployment/defaults/main.yml View File

@@ -4,6 +4,8 @@ metalsmith_capabilities: {}
4 4
 metalsmith_conductor_group:
5 5
 metalsmith_extra_args:
6 6
 metalsmith_image_checksum:
7
+metalsmith_image_kernel:
8
+metalsmith_image_ramdisk:
7 9
 metalsmith_netboot: false
8 10
 metalsmith_nics: []
9 11
 metalsmith_resource_class:

+ 12
- 4
roles/metalsmith_deployment/tasks/main.yml View File

@@ -24,6 +24,15 @@
24 24
       --ssh-public-key {{ ssh_key }}
25 25
     {% endfor %}
26 26
     --image {{ image }}
27
+    {% if image_checksum %}
28
+      --image-checksum {{ image_checksum }}
29
+    {% endif %}
30
+    {% if image_kernel %}
31
+      --image-kernel {{ image_kernel }}
32
+    {% endif %}
33
+    {% if image_ramdisk %}
34
+      --image-ramdisk {{ image_ramdisk }}
35
+    {% endif %}
27 36
     --hostname {{ instance.hostname }}
28 37
     {% if netboot %}
29 38
     --netboot
@@ -40,9 +49,6 @@
40 49
     {% for node in candidates %}
41 50
       --candidate {{ node }}
42 51
     {% endfor %}
43
-    {% if image_checksum %}
44
-      --image-checksum {{ image_checksum }}
45
-    {% endif %}
46 52
   when: state == 'present'
47 53
   vars:
48 54
     candidates: "{{ instance.candidates | default(metalsmith_candidates) }}"
@@ -50,7 +56,9 @@
50 56
     conductor_group: "{{ instance.conductor_group | default(metalsmith_conductor_group) }}"
51 57
     extra_args: "{{ instance.extra_args | default(metalsmith_extra_args) }}"
52 58
     image: "{{ instance.image | default(metalsmith_image) }}"
53
-    image: "{{ instance.image_checksum | default(metalsmith_image_checksum) }}"
59
+    image_checksum: "{{ instance.image_checksum | default(metalsmith_image_checksum) }}"
60
+    image_kernel: "{{ instance.image_kernel | default(metalsmith_image_kernel) }}"
61
+    image_ramdisk: "{{ instance.image_ramdisk | default(metalsmith_image_ramdisk) }}"
54 62
     netboot: "{{ instance.netboot | default(metalsmith_netboot) }}"
55 63
     nics: "{{ instance.nics | default(metalsmith_nics) }}"
56 64
     resource_class: "{{ instance.resource_class | default(metalsmith_resource_class) }}"

Loading…
Cancel
Save