Skip to content

Commit

Permalink
storage: add checks for physical volumes config
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Oct 8, 2024
1 parent 3824321 commit ea76bd1
Show file tree
Hide file tree
Showing 5 changed files with 814 additions and 75 deletions.
281 changes: 212 additions & 69 deletions service/lib/agama/storage/config_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,21 @@ def initialize(config)
#
# @return [Array<Issue>]
def issues
config.drives.flat_map { |d| drive_issues(d) } +
config.volume_groups.flat_map { |v| volume_group_issues(v) }
drives_issues + volume_groups_issues
end

private

# @return [Storage::Config]
attr_reader :config

# Issues from drives.
#
# @return [Array<Issue>]
def drives_issues
config.drives.flat_map { |d| drive_issues(d) }
end

# Issues from a drive config.
#
# @param config [Configs::Drive]
Expand All @@ -60,6 +66,94 @@ def drive_issues(config)
].flatten.compact
end

# Issue for not found device.
#
# @param config [Configs::Drive, Configs::Partition]
# @return [Agama::Issue]
def search_issue(config)
return if !config.search || config.found_device

if config.is_a?(Agama::Storage::Configs::Drive)
if config.search.skip_device?
warning(_("No device found for an optional drive"))
else
error(_("No device found for a mandatory drive"))
end
elsif config.search.skip_device?
warning(_("No device found for an optional partition"))
else
error(_("No device found for a mandatory partition"))
end
end

# Issues related to encryption.
#
# @param config [Configs::Drive, Configs::Partition, Configs::LogicalVolume]
# @return [Array<Issue>]
def encryption_issues(config)
return [] unless config.encryption

[
missing_encryption_password_issue(config.encryption),
unavailable_encryption_method_issue(config.encryption),
wrong_encryption_method_issue(config)
].compact
end

# @see #encryption_issues
#
# @param config [Configs::Encryption]
# @return [Issue, nil]
def missing_encryption_password_issue(config)
return unless config.missing_password?

error(
format(
# TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
# (e.g., 'luks1', 'random_swap').
_("No passphrase provided (required for using the method '%{crypt_method}')."),
crypt_method: config.method.to_human_string
)
)
end

# @see #encryption_issues
#
# @param config [Configs::Encryption]
# @return [Issue, nil]
def unavailable_encryption_method_issue(config)
method = config.method
return if !method || method.available?

error(
format(
# TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
# (e.g., 'luks1', 'random_swap').
_("Encryption method '%{crypt_method}' is not available in this system."),
crypt_method: method.to_human_string
)
)
end

# @see #encryption_issues
#
# @param config [Configs::Drive, Configs::Partition, Configs::LogicalVolume]
# @return [Issue, nil]
def wrong_encryption_method_issue(config)
method = config.encryption&.method
return unless method&.only_for_swap?
return if config.filesystem&.path == Y2Storage::MountPoint::SWAP_PATH.to_s

error(
format(
# TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
# (e.g., 'luks1', 'random_swap').
_("'%{crypt_method}' is not a suitable method to encrypt the device."),
crypt_method: method.to_human_string
)
)
end

# Issues from partitions.
#
# @param config [Configs::Drive]
Expand All @@ -79,15 +173,70 @@ def partition_issues(config)
].flatten.compact
end

# Issues from volume groups.
#
# @return [Array<Issue>]
def volume_groups_issues
[
overused_physical_volumes_devices_issues,
config.volume_groups.flat_map { |v| volume_group_issues(v) }
].flatten
end

# Issues for overused target devices for physical volumes.
#
# @note The Agama proposal is not able to calculate if the same target device is used by more
# than one volume group having several target devices.
#
# @return [Array<Issue>]
def overused_physical_volumes_devices_issues
overused = overused_physical_volumes_devices
return [] if overused.none?

overused.map do |device|
error(
format(
# TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
_("The device '%s' is used several times as target device for physical volumes"),
device
)
)
end
end

# Aliases of overused target devices for physical volumes.
#
# @return [Array<String>]
def overused_physical_volumes_devices
config.volume_groups
.map(&:physical_volumes_devices)
.map(&:uniq)
.select { |d| d.size > 1 }
.flatten
.tally
.select { |_, v| v > 1 }
.keys
end

# Issues from a volume group config.
#
# @param config [Configs::VolumeGroup]
# @return [Array<Issue>]
def volume_group_issues(config)
lvs_issues = config.logical_volumes.flat_map { |v| logical_volume_issues(v, config) }
pvs_issues = config.physical_volumes.map { |v| missing_physical_volume_issue(v) }.compact
[
logical_volumes_issues(config),
physical_volumes_issues(config),
physical_volumes_devices_issues(config),
physical_volumes_encryption_issues(config)
].flatten
end

lvs_issues + pvs_issues
# Issues from a logical volumes.
#
# @param config [Configs::VolumeGroup]
# @return [Array<Issue>]
def logical_volumes_issues(config)
config.logical_volumes.flat_map { |v| logical_volume_issues(v, config) }
end

# Issues from a logical volume config.
Expand All @@ -103,26 +252,6 @@ def logical_volume_issues(lv_config, vg_config)
].compact.flatten
end

# Issue for not found device.
#
# @param config [Configs::Drive, Configs::Partition]
# @return [Agama::Issue]
def search_issue(config)
return if !config.search || config.found_device

if config.is_a?(Agama::Storage::Configs::Drive)
if config.search.skip_device?
warning(_("No device found for an optional drive"))
else
error(_("No device found for a mandatory drive"))
end
elsif config.search.skip_device?
warning(_("No device found for an optional partition"))
else
error(_("No device found for a mandatory partition"))
end
end

# @see #logical_volume_issues
#
# @param lv_config [Configs::LogicalVolume]
Expand All @@ -141,13 +270,21 @@ def missing_thin_pool_issue(lv_config, vg_config)
error(
format(
# TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
_("There is no LVM thin pool volume with alias %s"),
_("There is no LVM thin pool volume with alias '%s'"),
lv_config.used_pool
)
)
end

# @see #logical_volume_issues
# Issues from physical volumes.
#
# @param config [Configs::VolumeGroup]
# @return [Array<Issue>]
def physical_volumes_issues(config)
config.physical_volumes.map { |v| missing_physical_volume_issue(v) }.compact
end

# @see #physical_volumes_issues
#
# @param pv_alias [String]
# @return [Issue, nil]
Expand All @@ -158,80 +295,86 @@ def missing_physical_volume_issue(pv_alias)
error(
format(
# TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
_("There is no LVM physical volume with alias %s"),
_("There is no LVM physical volume with alias '%s'"),
pv_alias
)
)
end

# Issues related to encryption.
# Issues from physical volumes devices (target devices).
#
# @param config [Configs::Drive, Configs::Partition, Configs::LogicalVolume]
# @param config [Configs::VolumeGroup]
# @return [Array<Issue>]
def encryption_issues(config)
return [] unless config.encryption

[
missing_encryption_password_issue(config),
available_encryption_method_issue(config),
wrong_encryption_method_issue(config)
].compact
def physical_volumes_devices_issues(config)
config.physical_volumes_devices
.map { |d| missing_physical_volumes_device_issue(d) }
.compact
end

# @see #encryption_issues
# @see #physical_volumes_devices_issue
#
# @param config [Configs::Drive, Configs::Partition, Configs::LogicalVolume]
# @param device_alias [String]
# @return [Issue, nil]
def missing_encryption_password_issue(config)
return unless config.encryption&.missing_password?
def missing_physical_volumes_device_issue(device_alias)
return if config.drives.any? { |d| d.alias == device_alias }

error(
format(
# TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
# (e.g., 'luks1', 'random_swap').
_("No passphrase provided (required for using the method '%{crypt_method}')."),
crypt_method: config.encryption.method.id.to_s
# TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
_("There is no target device for LVM physical volumes with alias '%s'"),
device_alias
)
)
end

# @see #encryption_issues
# Issues from physical volumes encryption.
#
# @param config [Configs::Drive, Configs::Partition, Configs::LogicalVolume]
# @return [Issue, nil]
def available_encryption_method_issue(config)
method = config.encryption&.method
return if !method || method.available?
# @param config [Configs::VolumeGroup]
# @return [Array<Issue>]
def physical_volumes_encryption_issues(config)
encryption = config.physical_volumes_encryption
return [] unless encryption

error(
format(
# TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
# (e.g., 'luks1', 'random_swap').
_("Encryption method '%{crypt_method}' is not available in this system."),
crypt_method: method.id.to_s
)
)
[
missing_encryption_password_issue(encryption),
unavailable_encryption_method_issue(encryption),
wrong_physical_volumes_encryption_method_issue(encryption)
].compact
end

# @see #encryption_issues
# @see #physical_volumes_encryption_issues
#
# @param config [Configs::Drive, Configs::Partition, Configs::LogicalVolume]
# @param config [Configs::Encryption]
# @return [Issue, nil]
def wrong_encryption_method_issue(config)
method = config.encryption&.method
return unless method&.only_for_swap?
return if config.filesystem&.path == Y2Storage::MountPoint::SWAP_PATH.to_s
def wrong_physical_volumes_encryption_method_issue(config)
method = config.method
return if method.nil? || valid_physical_volumes_encryption_method?(method)

error(
format(
# TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
# (e.g., 'luks1', 'random_swap').
_("'%{crypt_method}' is not a suitable method to encrypt the device."),
crypt_method: method.id.to_s
# (e.g., 'luks1').
_("'%{crypt_method}' is not a suitable method to encrypt the physical volumes."),
crypt_method: method.to_human_string
)
)
end

# Whether an encryption method can be used for encrypting physical volumes.
#
# @param method [Y2Storage::EncryptionMethod]
# @return [Boolean]
def valid_physical_volumes_encryption_method?(method)
valid_methods = [
Y2Storage::EncryptionMethod::LUKS1,
Y2Storage::EncryptionMethod::LUKS2,
Y2Storage::EncryptionMethod::PERVASIVE_LUKS2,
Y2Storage::EncryptionMethod::TPM_FDE
]

valid_methods.include?(method)
end

# Creates a warning issue.
#
# @param message [String]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def luks2?
def pervasive_luks2?
return false unless encryption_json.is_a?(Hash)

!encryption_json[:pervasive_luks2].nil?
!encryption_json[:pervasiveLuks2].nil?
end

# @return [Hash]
Expand Down Expand Up @@ -96,7 +96,7 @@ def luks2_conversions

# @return [Hash]
def pervasive_luks2_conversions
pervasive_json = encryption_json[:pervasive_luks2]
pervasive_json = encryption_json[:pervasiveLuks2]

{
method: Y2Storage::EncryptionMethod::PERVASIVE_LUKS2,
Expand Down
Loading

0 comments on commit ea76bd1

Please sign in to comment.