Skip to content

Commit 7cd32fe

Browse files
authored
Merge pull request #88 from openstack/stable/2024.1
2 parents bf51d34 + d11fa50 commit 7cd32fe

25 files changed

+4138
-160
lines changed

ironic_python_agent/agent.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,12 @@ def process_lookup_data(self, content):
467467
if config.get('metrics_statsd'):
468468
for opt, val in config.items():
469469
setattr(cfg.CONF.metrics_statsd, opt, val)
470+
if config.get('disable_deep_image_inspection') is not None:
471+
cfg.CONF.set_override('disable_deep_image_inspection',
472+
config['disable_deep_image_inspection'])
473+
if config.get('permitted_image_formats') is not None:
474+
cfg.CONF.set_override('permitted_image_formats',
475+
config['permitted_image_formats'])
470476
md5_allowed = config.get('agent_md5_checksum_enable')
471477
if md5_allowed is not None:
472478
cfg.CONF.set_override('md5_enabled', md5_allowed)

ironic_python_agent/config.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,13 +370,82 @@
370370
help='If the agent should rebuild the configuration drive '
371371
'using a local filesystem, instead of letting Ironic '
372372
'determine if this action is necessary.'),
373+
cfg.BoolOpt('disable_deep_image_inspection',
374+
default=False,
375+
help='This disables the additional deep image inspection '
376+
'the agent does before converting and writing an image. '
377+
'Generally, this should remain enabled for maximum '
378+
'security, but this option allows disabling it if there '
379+
'is a compatability concern.'),
380+
cfg.ListOpt('permitted_image_formats',
381+
default='raw,qcow2',
382+
help='The supported list of image formats which are '
383+
'permitted for deployment with Ironic Python Agent. If '
384+
'an image format outside of this list is detected, the '
385+
'image validation logic will fail the deployment '
386+
'process. This check is skipped if deep image '
387+
'inspection is disabled.'),
373388
]
374389

375-
CONF.register_cli_opts(cli_opts)
390+
disk_utils_opts = [
391+
cfg.IntOpt('efi_system_partition_size',
392+
default=550,
393+
help='Size of EFI system partition in MiB when configuring '
394+
'UEFI systems for local boot. A common minimum is ~200 '
395+
'megabytes, however OS driven firmware updates and '
396+
'unikernel usage generally requires more space on the '
397+
'efi partition.'),
398+
cfg.IntOpt('bios_boot_partition_size',
399+
default=1,
400+
help='Size of BIOS Boot partition in MiB when configuring '
401+
'GPT partitioned systems for local boot in BIOS.'),
402+
cfg.StrOpt('dd_block_size',
403+
default='1M',
404+
help='Block size to use when writing to the nodes disk.'),
405+
cfg.IntOpt('partition_detection_attempts',
406+
default=3,
407+
min=1,
408+
help='Maximum attempts to detect a newly created partition.'),
409+
cfg.IntOpt('partprobe_attempts',
410+
default=10,
411+
help='Maximum number of attempts to try to read the '
412+
'partition.'),
413+
cfg.IntOpt('image_convert_memory_limit',
414+
default=2048,
415+
help='Memory limit for "qemu-img convert" in MiB. Implemented '
416+
'via the address space resource limit.'),
417+
cfg.IntOpt('image_convert_attempts',
418+
default=3,
419+
help='Number of attempts to convert an image.'),
420+
]
421+
422+
disk_part_opts = [
423+
cfg.IntOpt('check_device_interval',
424+
default=1,
425+
help='After Ironic has completed creating the partition table, '
426+
'it continues to check for activity on the attached iSCSI '
427+
'device status at this interval prior to copying the image'
428+
' to the node, in seconds'),
429+
cfg.IntOpt('check_device_max_retries',
430+
default=20,
431+
help='The maximum number of times to check that the device is '
432+
'not accessed by another process. If the device is still '
433+
'busy after that, the disk partitioning will be treated as'
434+
' having failed.')
435+
]
376436

377437

378438
def list_opts():
379-
return [('DEFAULT', cli_opts)]
439+
return [('DEFAULT', cli_opts),
440+
('disk_utils', disk_utils_opts),
441+
('disk_partitioner', disk_part_opts)]
442+
443+
444+
def populate_config():
445+
"""Populate configuration. In a method so tests can easily utilize it."""
446+
CONF.register_cli_opts(cli_opts)
447+
CONF.register_opts(disk_utils_opts, group='disk_utils')
448+
CONF.register_opts(disk_part_opts, group='disk_partitioner')
380449

381450

382451
def override(params):
@@ -403,3 +472,6 @@ def override(params):
403472
LOG.warning('Unable to override configuration option %(key)s '
404473
'with %(value)r: %(exc)s',
405474
{'key': key, 'value': value, 'exc': exc})
475+
476+
477+
populate_config()
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright 2014 Red Hat, Inc.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
"""
17+
Code for creating partitions on a disk.
18+
19+
Imported from ironic-lib's disk_utils as of the following commit:
20+
https://opendev.org/openstack/ironic-lib/commit/42fa5d63861ba0f04b9a4f67212173d7013a1332
21+
"""
22+
23+
import logging
24+
25+
from ironic_lib.common.i18n import _
26+
from ironic_lib import exception
27+
from ironic_lib import utils
28+
from oslo_config import cfg
29+
30+
CONF = cfg.CONF
31+
32+
LOG = logging.getLogger(__name__)
33+
34+
35+
class DiskPartitioner(object):
36+
37+
def __init__(self, device, disk_label='msdos', alignment='optimal'):
38+
"""A convenient wrapper around the parted tool.
39+
40+
:param device: The device path.
41+
:param disk_label: The type of the partition table. Valid types are:
42+
"bsd", "dvh", "gpt", "loop", "mac", "msdos",
43+
"pc98", or "sun".
44+
:param alignment: Set alignment for newly created partitions.
45+
Valid types are: none, cylinder, minimal and
46+
optimal.
47+
48+
"""
49+
self._device = device
50+
self._disk_label = disk_label
51+
self._alignment = alignment
52+
self._partitions = []
53+
54+
def _exec(self, *args):
55+
# NOTE(lucasagomes): utils.execute() is already a wrapper on top
56+
# of processutils.execute() which raises specific
57+
# exceptions. It also logs any failure so we don't
58+
# need to log it again here.
59+
utils.execute('parted', '-a', self._alignment, '-s', self._device,
60+
'--', 'unit', 'MiB', *args, use_standard_locale=True)
61+
62+
def add_partition(self, size, part_type='primary', fs_type='',
63+
boot_flag=None, extra_flags=None):
64+
"""Add a partition.
65+
66+
:param size: The size of the partition in MiB.
67+
:param part_type: The type of the partition. Valid values are:
68+
primary, logical, or extended.
69+
:param fs_type: The filesystem type. Valid types are: ext2, fat32,
70+
fat16, HFS, linux-swap, NTFS, reiserfs, ufs.
71+
If blank (''), it will create a Linux native
72+
partition (83).
73+
:param boot_flag: Boot flag that needs to be configured on the
74+
partition. Ignored if None. It can take values
75+
'bios_grub', 'boot'.
76+
:param extra_flags: List of flags to set on the partition. Ignored
77+
if None.
78+
:returns: The partition number.
79+
80+
"""
81+
self._partitions.append({'size': size,
82+
'type': part_type,
83+
'fs_type': fs_type,
84+
'boot_flag': boot_flag,
85+
'extra_flags': extra_flags})
86+
return len(self._partitions)
87+
88+
def get_partitions(self):
89+
"""Get the partitioning layout.
90+
91+
:returns: An iterator with the partition number and the
92+
partition layout.
93+
94+
"""
95+
return enumerate(self._partitions, 1)
96+
97+
def commit(self):
98+
"""Write to the disk."""
99+
LOG.debug("Committing partitions to disk.")
100+
cmd_args = ['mklabel', self._disk_label]
101+
# NOTE(lucasagomes): Lead in with 1MiB to allow room for the
102+
# partition table itself.
103+
start = 1
104+
for num, part in self.get_partitions():
105+
end = start + part['size']
106+
cmd_args.extend(['mkpart', part['type'], part['fs_type'],
107+
str(start), str(end)])
108+
if part['boot_flag']:
109+
cmd_args.extend(['set', str(num), part['boot_flag'], 'on'])
110+
if part['extra_flags']:
111+
for flag in part['extra_flags']:
112+
cmd_args.extend(['set', str(num), flag, 'on'])
113+
start = end
114+
115+
self._exec(*cmd_args)
116+
117+
try:
118+
from ironic_python_agent import disk_utils # circular dependency
119+
disk_utils.wait_for_disk_to_become_available(self._device)
120+
except exception.IronicException as e:
121+
raise exception.InstanceDeployFailure(
122+
_('Disk partitioning failed on device %(device)s. '
123+
'Error: %(error)s')
124+
% {'device': self._device, 'error': e})

0 commit comments

Comments
 (0)