yast/yast-storage-ng

View on GitHub
src/lib/y2storage/planned/device.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Copyright (c) [2015-2019] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast"
require "y2storage/blk_device"
require "securerandom"

module Y2Storage
  module Planned
    # Abstract base class for the different devices templates in the
    # Planned namespace.
    #
    # Most classes in the {Planned} namespace represent an specification
    # of a given device that must be created in a devicegraph (thus, finally in
    # the system) by the storage proposal or by AutoYaST. That specification is,
    # of course, less concrete than the real device object.
    #
    # Those templates clases inherit from this and implement most of the
    # functionality and properties by composition, including several of the
    # mixins defined in the {Planned} namespace.
    #
    # FIXME: having separate setters for reuse_name and reuse_sid is dangerous
    # and makes some parts of the code cumbersome.
    # We need to fix this class to ensure they are in sync but adapting the mocks in
    # the test cases was too much work.
    class Device
      include Yast::Logger

      # @return [String] device name of an existing device to reuse for this
      #   purpose. As a result, some of the others attributes could be ignored.
      attr_accessor :reuse_name

      # @return [Integer] storage id of an existing device to reuse for this
      #   purpose. As a result, some of the others attributes could be ignored.
      #
      # @note This attribute is preferred over {reuse_name}.
      attr_accessor :reuse_sid

      # @return [String] planned device identifier. Planned devices are cloned/modified
      #   when calculating the proposal. This identifier allows to identify if two planned
      #   devices are related.
      attr_reader :planned_id

      def initialize
        @planned_id = SecureRandom.uuid
      end

      def to_s
        attrs = self.class.to_string_attrs.map do |attr|
          value = send(attr)
          value = "nil" if value.nil?
          "#{attr}=#{value}"
        end
        "#<#{self.class} " + attrs.join(", ") + ">"
      end

      def ==(other)
        other.class == self.class && other.internal_state == internal_state
      end

      # Attributes to display in {#to_s}
      #
      # This method is expected to be redefined in the subclasses
      #
      # @return [Array<Symbol>]
      def self.to_string_attrs
        [:reuse_name, :reuse_sid]
      end

      # Returns the (possibly processed) device to be used for the planned
      # device.
      #
      # FIXME: temporary API. It should be improved.
      def final_device!(plain_device)
        plain_device.encrypted? ? plain_device.encryption : plain_device
      end

      # Finds the device to reuse and sets its properties to match the planned device.
      #
      # @param devicegraph [Devicegraph] Device graph to adjust
      def reuse!(devicegraph)
        device = device_to_reuse(devicegraph)
        return unless device

        log.info "Reusing sid #{reuse_sid} (#{reuse_name}) (#{device.inspect}) for #{self}"
        reuse_device!(device)
      end

      # Whether this planned device is expected to reuse an existing one
      def reuse?
        !(reuse_name.nil? || reuse_name.empty?) || !reuse_sid.nil?
      end

      # Sets both #reuse_sid and #reuse_name according to the given device
      #
      # The description of the class states that having separate setters for both attributes
      # is unwanted but hard to fix. This method helps to overcome that problem.
      #
      # @param device [Y2Storage::Device]
      def assign_reuse(device)
        @reuse_sid = device.sid
        @reuse_name = device.name if device.respond_to?(:name)
      end

      # Determines whether the device is used as part of another device (VG, MD, Bcache, Btrfs, etc)
      #
      # This method is implemented by each mixin (e.g., CanBePv, CanBeMdMember, etc).
      #
      # @return [Boolean]
      def component?
        false
      end

      protected

      # @see reuse!
      #
      # Derived classed must define this method.
      #
      # @param _device [Device]
      def reuse_device!(_device)
        nil
      end

      def device_to_reuse(devicegraph)
        return nil unless reuse?

        device = devicegraph.find_device(reuse_sid) if reuse_sid
        device ||= BlkDevice.find_by_name(devicegraph, reuse_name)

        if device
          log.info "reused: sid #{device.sid} (#{reuse_name})"
        else
          log.info "reused device not found: sid #{reuse_sid} (#{reuse_name})"
        end

        device
      end

      def internal_state
        instance_variables.sort.map { |v| instance_variable_get(v) }
      end
    end
  end
end