_out_of_spec

in cookbooks/fb_storage/libraries/storage.rb [808:1135]


    def _out_of_spec
      
      missing_partitions = []
      
      missing_filesystems = []
      
      
      mismatched_partitions = []
      
      
      mismatched_filesystems = []
      
      missing_arrays = []
      
      mismatched_arrays = []
      
      incomplete_arrays = {}
      
      extra_arrays = []

      @arrays.each do |device, conf|
        short_device = File.basename(device)
        next if conf['_skip'] || conf['raid_level'] == 'hybrid_xfs'

        unless @existing_arrays.include?(short_device)
          Chef::Log.debug(
            "fb_storage: Array #{device} missing",
          )
          missing_arrays << device
          mismatched_filesystems << device
          next
        end

        existing_array = @existing_arrays[short_device]
        existing_device = @existing['by_device'][device]
        existing_members_set = Set.new(
          existing_array['members'].map { |x| "/dev/#{x}" },
        )
        if existing_array['journal']
          existing_members_set += Set.new(
            ["/dev/#{existing_array['journal']}"],
          )
        end
        desired_members_set = Set.new(conf['members'])
        if conf['journal']
          desired_members_set += Set.new([conf['journal']])
        end
        if existing_array['level'] != conf['raid_level']
          Chef::Log.warn(
            "fb_storage: Array #{device} has incorrect raid_level" +
            " #{existing_array['level']} vs #{conf['raid_level']}",
          )
          mismatched_arrays << device
          
          
          mismatched_filesystems << device
          next
        elsif existing_members_set != desired_members_set
          
          
          if existing_members_set < desired_members_set
            
            if [existing_array['level'], conf['raid_level']].include?(0)
              Chef::Log.warn(
                "fb_storage: Array #{device} is missing members, " +
                'but is RAID0 or should be RAID0 so treating it as a ' +
                "mismatched array. Existing: #{existing_members_set.to_a} " +
                "vs Desired: #{desired_members_set.to_a}",
              )
              mismatched_arrays << device
              
              
              mismatched_filesystems << device
              next
            else
              missing_members_set = desired_members_set - existing_members_set
              
              unless missing_members_set <= Set.new(@maintenance_disks)
                Chef::Log.info(
                  "fb_storage: Array #{device} is missing " +
                  "members: #{missing_members_set.to_a}",
                )
                incomplete_arrays[device] = missing_members_set.to_a
              end
            end
          
          
          else
            Chef::Log.warn(
              "fb_storage: Array #{device} has incorrect members" +
              " #{existing_members_set.to_a} vs #{desired_members_set.to_a}",
            )
            mismatched_arrays << device
            
            
            mismatched_filesystems << device
            next
          end
        end

        if !existing_device || !existing_device['fs_type']
          Chef::Log.warn(
            "fb_storage: Array #{device} has no FS",
          )
          missing_filesystems << device
        
        elsif existing_device['fs_type'] != conf['type']
          current_fs = existing_device ? existing_device['fs_type'] : '(none)'
          Chef::Log.warn(
            "fb_storage: Array #{device} has incorrect FS" +
            " #{current_fs} vs #{conf['type']}",
          )
          mismatched_filesystems << device
        end
      end

      
      @existing_arrays.each_key do |shortarray|
        array = "/dev/#{shortarray}"
        next if @arrays[array]

        Chef::Log.info("fb_storage: Extraneous array: #{array}")
        extra_arrays << array
      end

      
      @config.each do |device, conf|
        if @maintenance_disks.include?(device)
          Chef::Log.info(
            "fb_storage: Skipping check of #{device} because it " +
            'is marked as "in_maintenance"',
          )
          next
        end
        if conf['_skip']
          Chef::Log.info(
            "fb_storage: Skipping check of #{device} because it " +
            'is marked as "skip" in config.',
          )
          next
        end
        devparts = @existing['by_device'].to_hash.keys.select do |x|
          FB::Storage.device_name_from_partition(x) == device && x != device
        end
        
        devparts.sort_by! do |part|
          part.match('\d+$')[0].to_i
        end
        Chef::Log.debug(
          "fb_storage: partitions of #{device} are: #{devparts}",
        )

        dev_info = @existing['by_device'][device]

        if conf['whole_device']
          
          
          
          
          
          has_whole_disk_fs = devparts.count == 1 &&
            @existing['by_device'][devparts[0]].empty? &&
            dev_info['fs_type']
          if devparts.empty? || has_whole_disk_fs
            expected_label = conf['partitions'][0]['label']
            if dev_info.nil? || dev_info.empty?
              Chef::Log.debug(
                "fb_storage: Entire device #{device} needs " +
                'filesystem',
              )
              missing_filesystems << device
            elsif dev_info['fs_type'] != conf['partitions'][0]['type']
              Chef::Log.debug(
                "fb_storage: Entire device #{device} has " +
                'incorrect filesystem',
              )
              mismatched_filesystems << device
            elsif expected_label && dev_info['label'] != expected_label
              mismatched_filesystems << device
              Chef::Log.debug(
                "fb_storage: Entire device #{device} has " +
                "incorrect FS label. Expected #{expected_label}, found " +
                "#{dev_info['label']}.",
              )
            end
          else
            Chef::Log.debug(
              "fb_storage: Device #{device} has partitions " +
              ' but we want a whole-device filesystem',
            )
            
            mismatched_partitions << device
            mismatched_filesystems << device
          end
          next
        end

        
        
        if devparts.empty? && !(dev_info && dev_info['fs_type'])
          Chef::Log.info(
            "fb_storage: #{device} has no partitions and isn't " +
            'formatted',
          )
          missing_partitions << device
          missing_filesystems += partition_names(device, conf)
          next
        end

        
        
        if devparts.count != conf['partitions'].count
          Chef::Log.warn(
            "fb_storage: #{device} has the wrong number of " +
            "partitions (#{devparts.count} vs #{conf['partitions'].count})",
          )
          mismatched_partitions << device
          mismatched_filesystems += partition_names(device, conf)
          next
        end

        devparts.each_with_index do |part, index|
          Chef::Log.debug(
            "fb_storage: Considering partition #{part}",
          )
          
          if conf['partitions'][index]['_swraid_array'] ||
              conf['partitions'][index]['_swraid_array_journal']
            Chef::Log.debug('fb_storage: skipping swraid partition')
            next
          end

          
          
          if conf['partitions'][index]['_xfs_rt_data'] ||
              conf['partitions'][index]['_xfs_rt_rescue'] ||
              conf['partitions'][index]['_xfs_rt_metadata']
            expected_part_name = conf['partitions'][index]['part_name']
            actual_part_name = FB::Storage.get_actual_part_name(part)

            if actual_part_name != expected_part_name
              Chef::Log.warn("fb_storage: Partition #{part} expected to " +
                             "have partlabel '#{expected_part_name}', actual " +
                             "is '#{actual_part_name}'.")
              mismatched_partitions << device
            end
          end

          
          
          if conf['partitions'][index]['_xfs_rt_data'] ||
              conf['partitions'][index]['_xfs_rt_rescue']
            next
          end

          partinfo = @existing['by_device'][part]
          expected_fs = conf['partitions'][index]['type']
          expected_label = conf['partitions'][index]['label']
          if !expected_label && conf['partitions'][index]['_xfs_rt_metadata']
            
            
            expected_label = self.get_expected_label_for_hybrid_md_part(part)
          end

          if conf['partitions'][index]['_xfs_rt_metadata']
            expected_fs = 'xfs'
          end

          if !partinfo || !partinfo['fs_type']
            Chef::Log.warn(
              "fb_storage: Partition #{part} has no filesystem",
            )
            missing_filesystems << part
          elsif partinfo['fs_type'] != expected_fs
            Chef::Log.warn(
              "fb_storage: Partition #{part} has the wrong " +
              "filesystem (#{partinfo['fs_type']} vs #{expected_fs})",
            )
            mismatched_filesystems << part
          elsif expected_label && partinfo['label'] != expected_label
            Chef::Log.warn(
              "fb_storage: Partition #{part} has incorrect " +
              "label. Expected #{expected_label}, found " +
              "#{partinfo['label']}.",
            )
            mismatched_filesystems << part
          end
        end
      end

      
      
      
      
      
      
      missing_filesystems.each do |fs|
        mma = @arrays.select do |_array, config|
          config['raid_level'] == 'hybrid_xfs' &&
            (config['members'].include?(fs) ||
             config['journal'] == fs)
        end
        missing_filesystems += mma.keys
      end

      mismatched_filesystems.each do |fs|
        mma = @arrays.select do |_array, config|
          config['raid_level'] == 'hybrid_xfs' &&
            (config['members'].include?(fs) ||
             config['journal'] == fs)
        end
        mismatched_filesystems += mma.keys
      end

      
      {
        :mismatched_partitions => mismatched_partitions.sort.uniq,
        :mismatched_filesystems => mismatched_filesystems.sort.uniq,
        :missing_partitions => missing_partitions.sort.uniq,
        :missing_filesystems => missing_filesystems.sort.uniq,
        :missing_arrays => missing_arrays.sort.uniq,
        :mismatched_arrays => mismatched_arrays.sort.uniq,
        :extra_arrays => extra_arrays.sort.uniq,
        
        :incomplete_arrays => incomplete_arrays,
      }
    end