theforeman/foreman

View on GitHub
app/helpers/hosts_helper.rb

Summary

Maintainability
C
1 day
Test Coverage
module HostsHelper
  include OperatingsystemsHelper
  include HostsAndHostgroupsHelper
  include ComputeResourcesVmsHelper
  include HostsNicHelper
  include BMCHelper
  include AuthorizeHelper

  def provider_partial_exist?(compute_resource, partial)
    return false unless compute_resource

    compute_resource_name = compute_resource.provider.downcase
    ActionController::Base.view_paths.any? do |path|
      File.exist?(File.join(path, 'compute_resources_vms', 'form', compute_resource_name, "_#{partial}.html.erb"))
    end
  end

  def provider_partial(compute_resource, partial)
    return nil unless compute_resource

    compute_resource_name = compute_resource.provider.downcase
    "compute_resources_vms/form/#{compute_resource_name}/#{partial}"
  end

  def provision_method_partial_exist?(provision_method, partial)
    return false unless provision_method

    ActionController::Base.view_paths.any? do |path|
      File.exist?(File.join(path, 'hosts', 'provision_method', provision_method, "_#{partial}.html.erb"))
    end
  end

  def provision_method_partial(provision_method, partial)
    return nil unless provision_method

    "hosts/provision_method/#{provision_method}/#{partial}"
  end

  def compute_specific_js(compute_resource, js_name)
    javascript_include_tag("compute_resources/#{compute_resource.provider.downcase}/#{js_name}.js")
  end

  def host_taxonomy_select(f, taxonomy)
    taxonomy_id = "#{taxonomy.to_s.downcase}_id"
    selected_taxonomy = @host.new_record? ? taxonomy.current.try(:id) : @host.send(taxonomy_id)
    label = _(taxonomy.to_s)
    select_opts = { :include_blank => !@host.managed? || @host.send(taxonomy_id).nil?,
                    :selected => selected_taxonomy }
    html_opts = { :disabled => !@host.new_record?,
                  :class => 'host-taxonomy-select',
                  :label => label,
                  :'data-host-id' => @host.id,
                  :'data-url' => process_taxonomy_hosts_path,
                  :help_inline => :indicator,
                  :required => true }
    html_opts[:label_help] = (_("%s can be changed using bulk action on the All Hosts page") % taxonomy) unless @host.new_record?

    select_f f, taxonomy_id.to_sym, taxonomy.send("my_#{taxonomy.to_s.downcase.pluralize}"), :id, :to_label,
      select_opts, html_opts
  end

  def flags_for_nic(nic)
    flags = ""
    flags += "<i class=\"nic-flag glyphicon glyphicon glyphicon-tag\" title=\"#{_('Primary')}\"></i>" if nic.primary?
    flags += "<i class=\"nic-flag glyphicon glyphicon glyphicon-hdd\" title=\"#{_('Provisioning')}\"></i>" if nic.provision?
    flags.html_safe
  end

  def last_report_column(record)
    time = record.last_report? ? date_time_relative_value(record.last_report) : ""
    link_to_if_authorized(time,
      hash_for_host_config_report_path(:host_id => record.to_param, :id => "last"),
      last_report_tooltip(record))
  end

  def last_report_tooltip(record)
    opts = { :rel => "twipsy" }
    date = record.last_report.nil? ? '' : date_time_absolute_value(record.last_report) + ", "
    if @last_report_ids[record.id]
      opts["data-original-title"] = date + _("view last report details")
    else
      opts.merge!(:disabled => true, :class => "disabled", :onclick => 'return false')
      opts["data-original-title"] = date + _("report already deleted") unless record.last_report.nil?
    end
    opts
  end

  def host_owner_column(host)
    case host.owner_type
    when 'User'
      icon_text('user', host.owner, :kind => 'fa')
    when 'Usergroup'
      icon_text('users', host.owner, :kind => 'fa')
    end
  end

  # method that reformat the hostname column by adding the status icons
  def name_column(host)
    style = host_global_status_icon_class_for_host(host)
    displayable_statuses = host.host_statuses.select { |status| status.relevant? && !status.substatus? }
    tooltip = displayable_statuses.sort_by(&:type).map { |status| "#{_(status.name)}: #{_(status.to_label)}" }.join(', ')

    content = content_tag(:span, "", {:rel => "twipsy", :class => style, :"data-original-title" => tooltip})
    content += link_to("  #{host}", current_host_details_path(host))
    content
  end

  def host_global_status_icon_class_for_host(host)
    options = {}
    options[:last_reports] = @last_reports unless @last_reports.nil?
    host_global_status_icon_class(host.build_global_status(options).status)
  end

  def host_global_status_icon_class(status)
    icon_class = case status
                 when HostStatus::Global::OK
                   'pficon-ok'
                 when HostStatus::Global::WARN
                   'pficon-info'
                 when HostStatus::Global::ERROR
                   'pficon-error-circle-o'
                 else
                   'pficon-help'
                 end

    "host-status #{icon_class} #{host_global_status_class(status)}"
  end

  def host_global_status_class(status)
    case status
      when HostStatus::Global::OK
        'status-ok'
      when HostStatus::Global::WARN
        'status-warn'
      when HostStatus::Global::ERROR
        'status-error'
      else
        'status-question'
    end
  end

  def host_global_status_link_class(status)
    if status.status_link
      case status.to_global
      when HostStatus::Global::OK
        'status-ok-link'
      when HostStatus::Global::WARN
        'status-warn-link'
      when HostStatus::Global::ERROR
        'status-error-link'
      else
        'status-question-link'
      end
    else
      host_global_status_class(status.to_global)
    end
  end

  def days_ago(time)
    ((Time.zone.now - time) / 1.day).ceil.to_i
  end

  def searching?
    params[:search].empty?
  end

  def multiple_actions
    actions = []
    UI::HostDescription.reduce_providers(:multiple_actions).each do |provider|
      actions += send(provider)
    end
    prioritized_members(actions, :action)
  end

  def multiple_actions_select
    select_action_button(_("Select Action"), {:id => 'submit_multiple'},
      multiple_actions.map do |action|
        # If the action array has 3 entries, the third one is whether to use a modal dialog or not
        modal = (action.size == 3) ? action[3] : true
        if modal
          link_to_function(action[0], "tfm.hosts.table.buildModal(this, '#{action[1]}')", :'data-dialog-title' => _("%s - The following hosts are about to be changed") % action[0])
        else
          link_to_function(action[0], "tfm.hosts.table.buildRedirect('#{action[1]}')")
        end
      end.flatten
    )
  end

  def date(ts = nil)
    ts ? date_time_relative_value(ts) : _("N/A")
  end

  def template_path(opts = {})
    if (t = @host.provisioning_template(opts))
      link_to t, edit_provisioning_template_path(t)
    else
      _("N/A")
    end
  end

  def selected?(host)
    return false if host.nil? || !host.is_a?(Host::Base) || session[:selected].nil?
    session[:selected].include?(host.id.to_s)
  end

  def resources_chart_data(timerange = 1.day.ago)
    time = ['time']
    applied = [_("Applied")]
    failed = [_("Failed")]
    restarted = [_("Failed restarts")]
    failed_restarts = [_("Skipped")]
    skipped = [_("Restarted")]
    @host.reports.recent(timerange).each do |r|
      time            << r.reported_at.to_i
      applied         << r.applied
      failed          << r.failed
      restarted       << r.restarted
      failed_restarts << r.failed_restarts
      skipped         << r.skipped
    end
    [time, applied, failed, failed_restarts, skipped, restarted]
  end

  def runtime_chart_data(timerange = 1.day.ago)
    time = ['time']
    config = [_("Config Retrieval")]
    runtime = [_("Runtime")]
    @host.reports.recent(timerange).each do |r|
      time << r.reported_at.to_i
      config  << r.config_retrieval
      runtime << r.runtime
    end
    [time, config, runtime]
  end

  def reports_show
    return if @host.reports.empty?
    number_of_days = days_ago(@host.reports.order(:reported_at).first.reported_at)
    width = [number_of_days.to_s.size + 2, 4].max

    form_tag @host, :id => 'days_filter', :method => :get, :class => "form form-inline" do
      content_tag(:span, (_("Found %{count} reports from the last %{days} days") %
        { :days => select(nil, 'range', 1..number_of_days,
          {:selected => @range}, {:style => "float:none; width: #{width}em;", :onchange => "$('#days_filter').submit();$(this).disabled();"}),
          :count => @host.reports.recent(@range.days.ago).count }).html_safe)
    end
  end

  def name_field(host)
    return if host.name.blank?
    host.managed? ? host.shortname : host.name
  end

  def build_duration(host)
    return _('N/A') if host.initiated_at.nil? || host.installed_at.nil?
    distance_of_time_in_words(host.initiated_at, host.installed_at, include_seconds: true)
  end

  def overview_fields(host)
    fields = []
    UI::HostDescription.reduce_providers(:overview_fields).each do |provider|
      fields += send(provider, host)
    end
    prioritized_members(fields, :field)
  end

  def possible_images(cr, arch = nil, os = nil)
    return cr.images.order(:name) unless controller_name == "hosts"
    return [] unless arch && os
    cr.images.where(:architecture_id => arch, :operatingsystem_id => os).order(:name)
  end

  def state(s)
    s ? ' ' + _("Off") : ' ' + _("On")
  end

  def host_title_actions(host)
    actions = []
    UI::HostDescription.reduce_providers(:title_actions).each do |provider|
      actions += send(provider, host)
    end
    title_actions(
      prioritized_members(actions, :action)
    )
  end

  def delete_host_dialog(host)
    if host.compute?
      if Setting[:destroy_vm_on_host_delete]
        _("Are you sure you want to delete host %s? This will delete the VM and its disks, and is irreversible. This behavior can be changed via global setting \"Destroy associated VM on host delete\".") % host.name
      else
        _("Are you sure you want to delete host %s? It is irreversible, but VM and its disks will not be deleted. This behavior can be changed via global setting \"Destroy associated VM on host delete\".") % host.name
      end
    else
      _("Are you sure you want to delete host %s? This action is irreversible.") % host.name
    end
  end

  # we ignore interfaces.conflict because they are always registered in host errors as well
  def conflict_objects(errors)
    errors.attribute_names.map(&:to_s).select { |key| key =~ /conflict$/ && key != 'interfaces.conflict' }.map(&:to_sym)
  end

  def has_conflicts?(errors)
    conflict_objects(errors).each do |c|
      return true if errors[c.to_sym].any?
    end
    false
  end

  def has_dhcp_lease_errors?(errors)
    errors.include?(:dhcp_lease_error)
  end

  def show_appropriate_host_buttons(host)
    priority_buttons = []
    UI::HostDescription.reduce_providers(:overview_buttons).each do |provider|
      priority_buttons += send(provider, host)
    end

    prioritized_members(priority_buttons, :button)
  end

  def allocation_text_f(f)
    active = 'Size'
    active = 'None' if f.object.allocation.to_i == 0
    active = 'Full' if f.object.allocation == f.object.capacity
    text_f f, :allocation, :class => "input-mini", :label => _("Allocation (GB)"), :label_size => "col-md-2",
    :readonly => (active == 'Size') ? false : true,
    :help_inline => (content_tag(:span, :class => 'btn-group', :'data-toggle' => 'buttons-radio') do
      [N_('None'), N_('Size'), N_('Full')].collect do |label|
        content_tag :button, _(label), :type => 'button', :href => '#',
          :name => 'allocation_radio_btn',
          :class => (label == active) ? 'btn btn-default active' : 'btn btn-default',
          :onclick => "tfm.computeResource.libvirt.allocationSwitcher(this, '#{label}');",
          :data => { :toggle => 'button' },
          :id => (label == 'Full') ? 'btnAllocationFull' : nil
      end.join(' ').html_safe
    end)
  end

  # helper method to provide data attribute if subnets has ipam enabled / disabled
  def subnets_ipam_data(field)
    data = {}
    domain_subnets(field).each do |subnet|
      data[subnet.id] = { :ipam => subnet.ipam? }
    end
    data
  end

  def remove_interface_link(f)
    remove_child_link('x', f, {:rel => 'twipsy',
                               :'data-title' => _('remove network interface'),
                               :'data-placement' => 'left',
                               :class => 'fr label label-danger'})
  end

  def link_status(nic)
    return '' if nic.new_record?

    if nic.link
      status = '<i class="glyphicon glyphicon glyphicon-arrow-up interface-up" title="' + _('Interface is up') + '"></i>'
    else
      status = '<i class="glyphicon glyphicon glyphicon-arrow-down interface-down" title="' + _('Interface is down') + '"></i>'
    end
    status.html_safe
  end

  def interface_flags(nic)
    primary_class = nic.primary? ? "active" : ""
    provision_class = nic.provision? ? "active" : ""

    status = "<i class=\"glyphicon glyphicon glyphicon-tag primary-flag #{primary_class}\" title=\"#{_('Primary')}\"></i>"
    status += "<i class=\"glyphicon glyphicon glyphicon-hdd provision-flag #{provision_class}\" title=\"#{_('Provisioning')}\"></i>"
    status.html_safe
  end

  def build_state(build)
    build.state ? 'warning' : 'danger'
  end

  def review_build_button(form, status)
    form.submit(_("Build"),
      :class => "btn btn-#{status} submit",
      :title => (status == 'warning') ? _('Build') : _('Errors occurred, build may fail')
    )
  end

  def build_error_link(type, id)
    case type
      when :templates
        link_to_if_authorized(_("Edit"), hash_for_edit_provisioning_template_path(:id => id).merge(:auth_object => id),
          :class => "btn btn-default btn-xs pull-right", :title => _("Edit %s" % type))
    end
  end

  def inherited_by_default?(field, host)
    return false unless host.hostgroup && host.hostgroup_id_was.nil?
    return false if params[:action] == 'clone'
    return true unless params[:host]
    !params[:host][field]
  end

  def multiple_proxy_select(form, proxy_feature)
    selectable_f form,
      :proxy_id,
      [[_("Select desired %s proxy") % _(proxy_feature), "disabled"]] +
      [[_("*Clear %s proxy*") % _(proxy_feature), ""]] +
      SmartProxy.with_features(proxy_feature).map { |p| [p.name, p.id] },
      {},
      {:label => _(proxy_feature), :onchange => "tfm.hosts.table.toggleMultipleOkButton(this)" }
  end

  def randomize_mac_link
    if NameGenerator.random_based?
      link_to_function(icon_text('random'), 'randomizeName()', :class => 'btn btn-default',
        :title => _('Generate new random name. Visit Settings to disable this feature.'))
    end
  end

  def host_breadcrumb
    breadcrumbs(resource_url: "/api/v2/hosts?thin=true'")
  end

  def prioritized_members(list, value_key)
    list.
      sort_by { |member| member[:priority] }.
      map { |member_hash| member_hash[value_key] }
  end

  def lookup_values_data(host_or_hostgroup)
    host_or_hostgroup.lookup_values.map do |item|
      {
        :id => item.id,
        :lookup_key_id => item.lookup_key_id,
        :value => item.value,
        :errors => item.errors.to_hash,
      }
    end
  end

  def virtual?(host)
    return unless host.reported_data

    host.reported_data.virtual ? _('Yes') : _('No')
  end

  def humanize_bytes(number, from: nil)
    return unless number

    number = case from
             when :mega
               number * 1.megabyte
             else
               number
             end

    number_to_human_size(number)
  end
end