ManageIQ/manageiq-providers-ibm_cloud

View on GitHub
app/models/manageiq/providers/ibm_cloud/vpc/cloud_manager/provision_workflow/volumes.rb

Summary

Maintainability
A
1 hr
Test Coverage
F
29%
# frozen_string_literal: true

# Contains the elements used to populate and validate boot volume, attached volumes and new volume related fields.
module ManageIQ::Providers::IbmCloud::VPC::CloudManager::ProvisionWorkflow::Volumes
  # These are the keys that are populated in the Volumes tab of the UI.
  # @return [Array<String>]
  def volume_dialog_keys
    %i[volume_name volume_profile volume_size volume_on_instance_delete].freeze
  end

  # Fetch volume profiles from inventory.
  # @param _options [void]
  # @return [Hash] Hash with ems_ref as key and name as value.
  def storage_type_to_profile(_options = {})
    return {} if ar_ems.nil?

    @storage_type_to_profile ||= string_dropdown(ar_ems.cloud_volume_types, :remove_fields => %w[custom])
  rescue => e
    logger(__method__).ui_exception(e)
  end

  # Wait until zone is set, then fetch volumes that have a status of available and reside in that zone.
  # @param _options [void]
  # @return [Hash] Hash with ems_ref as key and name as value.
  def cloud_volumes_to_volumes(_options = {})
    return {} if ar_ems.nil?

    zone = field(:placement_availability_zone)
    return {} if zone.nil?

    ar_volumes = ar_ems.cloud_volumes.select { |cloud_volume| cloud_volume[:status] == 'available' && cloud_volume[:availability_zone_id] == zone }
    string_dropdown(ar_volumes)
  rescue => e
    logger(__method__).ui_exception(e)
  end

  # Perform any needed manipulation and validation for new volume fields.
  # @param values [Hash<Symbol, String>] Hash of all the keys and values submitted in the form.
  # @return [void]
  def parse_new_volumes_fields(values)
    values[:new_volumes] = create_new_volumes_array(values)
    validate_volumes(values[:new_volumes])
  end

  # Select keys that have one of the volume dialog keys followed by a digit.
  # @param values [Hash<Symbol, String>] Hash of all the keys and values submitted in the form.
  # @return [Array<Hash{Symbol => String, Integer}>]
  def create_new_volumes_array(values)
    new_volumes = []
    # Regex to place the field name in the field index and count in the count index.
    keys_regex = /(?<field>#{volume_dialog_keys.join('|')})_(?<count>\d+)/

    values.each do |key, value|
      next if value.nil? || (value.respond_to?(:length) && value.length.zero?)

      reg = key.to_s.match(keys_regex) # 'req' should be nil or a match instance. Match instance has ':field' and ':count' keys.
      next if reg.nil?

      count = reg[:count].to_i
      new_volumes[count] ||= {}

      field = reg[:field].to_sym

      # Use integer if value starts and ends with digits.
      new_volumes[count][field] = value.to_s.match?(/^\d+$/) ? value.to_i : value
    end

    # The volume index starts at 1. Which means index 0 is nil. Compact to get rid of it.
    new_volumes.compact
  rescue => e
    logger(__method__).ui_exception(e, :context_msg => 'Creating new_volume array.')
  end

  # Validate the new volumes fields.
  # @param new_volumes [Array<Hash{Symbol => String, Integer}>]
  # @raise [StandardError] Validation of fields has failed. The error message contains a list of validation errors.
  # @return [void]
  def validate_volumes(new_volumes)
    error_array = []
    new_volumes.each_with_index do |item, zero_index|
      index = zero_index + 1
      validate_volume_required(index, item, error_array)
      validate_volume_name(index, item[:volume_name], error_array)
      validate_volume_size(index, item[:volume_size], error_array)
      validate_volume_profile(index, item[:volume_profile], error_array)
    end
    return nil if error_array.empty?

    e_msg = _("New volumes has the following problems: %{error}") % {:error => error_array.join(", ")}
    raise MiqException::MiqProvisionError, e_msg
  end

  # Validate that volume_name and volume_size are populated. Add a string explaining each violation to error_array.
  # @param index [Integer] Number to print in error messages. Denotes the current UI volume being checked.Denotes the current UI volume being checked.
  # @param item [Hash<Symbol, String>] A new_volume hash.
  # @param error_array [Array<String>] An array of validation error strings.
  # @return [void]
  def validate_volume_required(index, item, error_array)
    logger(__method__).debug("Item is #{item}")
    %i[volume_name volume_size].each do |req|
      next if item.key?(req) # Skip if new_volume hash contains the key the required key.

      e_str = _("Volume %{index} %{field} is required.") % {:index => index, :field => req.to_s}
      error_array.append(e_str)
    end
  end

  # Validate that volume_name conforms to 2 lower-case characters followed by a random number of characters, numbers or dashes.
  # On validation error, add a string explaining the error to error_array.
  # @param index [Integer] Number to print in error messages. Denotes the current UI volume being checked.
  # @param value [String] The value for the volume_name field.
  # @param error_array [Array<String>] An array of validation error strings.
  # @return [void]
  def validate_volume_name(index, value, error_array)
    return nil if value.nil? || value.to_s.match?(/^[a-z][a-z][-a-z0-9]*[a-z0-9]$/)

    e_msg = _("Volume %{index} name '%{value}' must be 3 characters or greater, all lower case, start with two characters, followed by characters, numbers or dash.") % {:index => index, :value => value}
    error_array.append(e_msg)
  end

  # Validate that volume_size is a number between 10 and 2000.
  # On validation error, add a string explaining the error to error_array.
  # @param index [Integer] Number to print in error messages. Denotes the current UI volume being checked.
  # @param value [Integer] The value for the volume_size field.
  # @param error_array [Array<String>] An array of validation error strings.
  # @return [void]
  def validate_volume_size(index, value, error_array)
    return nil if value.nil? || value.to_i.between?(10, 2000)

    e_msg = _("Volume %{index} size '%{value}' must be between 10 and 2000.") % {:index => index, :value => value}
    error_array.append(e_msg)
  end

  # Validate that volume_profile field has a known volume_profile name.
  # On validation error, add a string explaining the error to error_array.
  # @param index [Integer] Number to print in error messages. Denotes the current UI volume being checked.
  # @param value [String] The value for the volume_profile field.
  # @param error_array [Array<String>] An array of validation error strings.
  # @return [void]
  def validate_volume_profile(index, value, error_array)
    return nil if value.nil? || value.to_s.length.zero?

    s_values = storage_type_to_profile.values
    return nil if s_values.include?(value)

    s_str = s_values.join(', ')
    e_msg = _("Volume %{index} profile '%{value}' must be one of %{types}.") % {:index => index, :value => value, :types => s_str}
    error_array.append(e_msg)
  end
end