Project

General

Profile

Download (17.2 KB) Statistics
| Branch: | Tag: | Revision:
class HostsController < ApplicationController
include Facts
include Foreman::Controller::HostDetails
include Foreman::Controller::AutoCompleteSearch

# actions which don't require authentication and are always treathed as the admin user
ANONYMOUS_ACTIONS=[ :query, :externalNodes, :lookup ]
skip_before_filter :require_login, :only => ANONYMOUS_ACTIONS
skip_before_filter :require_ssl, :only => ANONYMOUS_ACTIONS
skip_before_filter :authorize, :only => ANONYMOUS_ACTIONS
before_filter :set_admin_user, :only => ANONYMOUS_ACTIONS

before_filter :find_hosts, :only => :query
before_filter :ajax_methods, :only => [:hostgroup_or_environment_selected]
before_filter :find_multiple, :only => [:update_multiple_parameters, :multiple_build,
:select_multiple_hostgroup, :select_multiple_environment, :multiple_parameters, :multiple_destroy,
:multiple_enable, :multiple_disable, :submit_multiple_disable, :submit_multiple_enable, :update_multiple_hostgroup,
:update_multiple_environment, :submit_multiple_build, :submit_multiple_destroy]
before_filter :find_by_name, :only => %w[show edit update destroy puppetrun setBuild cancelBuild report
reports facts storeconfig_klasses clone externalNodes pxe_config toggle_manage]
after_filter :disconnect_from_hypervisor, :only => :hypervisor_selected

filter_parameter_logging :root_pass
helper :hosts, :reports

def index (title = nil)
begin
# restrict allowed hosts list based on the user permissions
my_hosts = User.current.admin? ? Host : Host.my_hosts
search = my_hosts.search_for(params[:search],:order => params[:order], :group => 'hosts.id')
rescue => e
error e.to_s
search = my_hosts.search_for ''
end
respond_to do |format|
format.html do
@hosts = search.paginate :page => params[:page], :include => included_associations
# SQL optimizations queries
@last_reports = Report.maximum(:id, :group => :host_id, :conditions => {:host_id => @hosts})
@fact_kernels = FactValue.all(:select => "host_id, fact_values.value", :joins => [:host, :fact_name],
:conditions => {"fact_values.host_id" => @hosts, "fact_names.name" => 'kernel'})
# rendering index page for non index page requests (out of sync hosts etc)
render :index if title and @title = title
end
format.json { render :json => search.all(:select => "hosts.name", :include => included_associations).map(&:name) }
format.yaml { render :text => search.all(:select => "hosts.name", :include => included_associations).map(&:name).to_yaml }
end
end

def show
respond_to do |format|
format.html {
# filter graph time range
@range = (params["range"].empty? ? 7 : params["range"].to_i)

# summary report text
@report_summary = Report.summarise(@range.days.ago, @host)
}
format.yaml { render :text => @host.info.to_yaml }
format.json { render :json => @host }
end
end

def new
@host = Host.new :managed => true
@host.host_parameters.build
end

# Clone the host
def clone
new = @host.clone
load_vars_for_ajax
new.puppetclasses = @host.puppetclasses
# Clone any parameters as well
@host.host_parameters.each{|param| new.host_parameters << param.clone}
flash[:error_customisation] = {:header_message => nil, :class => "flash notice", :id => nil,
:message => "The following fields will need reviewing:" }
new.valid?
@host = new
render :action => :new
end

def create
@host = Host.new(params[:host])
@host.managed = true
forward_request_url
if @host.save
process_success :success_redirect => @host
else
load_vars_for_ajax
process_error
end
end

def edit
load_vars_for_ajax
end

def update
forward_request_url
if @host.update_attributes(params[:host])
process_success :success_redirect => @host
else
load_vars_for_ajax
process_error
end
end

def destroy
if @host.destroy
process_success
else
process_error
end
end

# form AJAX methods

def domain_selected
assign_parameter "domain"
end

def hostgroup_or_environment_selected
@environment = Environment.find(params[:environment_id]) if params[:environment_id].to_i > 0
@hostgroup = Hostgroup.find(params[:hostgroup_id]) if params[:hostgroup_id].to_i > 0
if @environment or @hostgroup
@host ||= Host.new
@host.hostgroup = @hostgroup if @hostgroup
@host.environment = @environment if @environment
render :partial => 'puppetclasses/class_selection', :locals => {:obj => (@host)}
else
return head(:not_found)
end
end

def hypervisor_selected
hypervisor_id = params[:host_hypervisor_id].to_i

# bare metal selected
hypervisor_defaults and return if hypervisor_id == 0

@host ||= Host.new
if ((@host.hypervisor_id = hypervisor_id) > 0) and (@hypervisor = Hypervisor.find(@host.hypervisor_id))
begin
@hypervisor.connect
rescue => e
# we reset to default
@host.hypervisor_id = nil
logger.warn e.to_s
hypervisor_defaults(e.to_s) and return
end

@guest = Virt::Guest.new({:name => (@host.try(:name) || "new-#{Time.now}.to_i")})

render :update do |page|
page.replace_html :virtual_machine, :partial => "hypervisor"
page << "if ($('host_mac')) {"
page.remove :host_mac_label
page.remove :host_mac
page << " }"
end
else
return head(:not_found)
end
end

#returns a yaml file ready to use for puppet external nodes script
#expected a fqdn parameter to provide hostname to lookup
#see example script in extras directory
#will return HTML error codes upon failure

def externalNodes
@host ||= Host.find_by_name(params[:name]) if params[:name]
not_found and return unless @host

begin
respond_to do |format|
format.html { render :text => @host.info.to_yaml.gsub("\n","<br/>") }
format.yml { render :text => @host.info.to_yaml }
end
rescue
# failed
logger.warn "Failed to generate external nodes for #{@host} with #{$!}"
render :text => 'Unable to generate output, Check log files\n', :status => 412 and return
end
end

def puppetrun
if GW::Puppet.run @host.name
notice "Successfully executed, check log files for more details"
else
error "Failed, check log files"
end
redirect_to host_path(@host)
end

def setBuild
forward_request_url
if @host.setBuild != false
process_success :success_msg => "Enabled #{@host.name} for rebuild on next boot", :success_redirect => :back
else
process_error :redirect => :back, :error_msg => "Failed to enable #{@host.name} for installation: #{@host.errors.full_messages.join("br")}"
end
end

def cancelBuild
if @host.built(false)
process_success :success_msg => "Canceled pending build for #{@host.name}", :success_redirect => :back
else
process_error :redirect => :back, :error_msg => "Failed to cancel pending build for #{@host.name}"
end
end

def query
if @verbose
@hosts.map! do |host|
hash = {}
h = Host.find_by_name host
hash[host] = h.info
hash[host]["facts"]= h.facts_hash
hash
end
end
respond_to do |format|
format.html
format.yml { render :text => @hosts.to_yaml }
end
end

def toggle_manage
if @host.toggle! :managed
toggle_text = @host.managed ? "" : " no longer"
process_success :success_msg => "Foreman now#{toggle_text} manages the build cycle for #{@host.name}", :success_redirect => :back
else
process_error :error_msg => "Failed to modify the build cycle for #{@host.name}", :redirect => :back
end
end

def pxe_config
redirect_to(:controller => "unattended", :action => "pxe_#{@host.operatingsystem.pxe_type}_config", :host_id => @host) if @host
end

def storeconfig_klasses
end

# multiple host selection methods

def multiple_parameters
@parameters = HostParameter.reference_id_is(@hosts).all(:select => "distinct name")
end

def update_multiple_parameters
if params[:name].empty?
notice "No parameters were allocated to the selected hosts, can't mass assign."
redirect_to hosts_path and return
end

@skipped_parameters = {}
counter = 0
@hosts.each do |host|
skipped = []
params[:name].each do |name, value|
next if value.empty?
if host_param = host.host_parameters.find_by_name(name)
counter += 1 if host_param.update_attribute(:value, value)
else
skipped << name
end
@skipped_parameters[host.name] = skipped unless skipped.empty?
end
end
if @skipped_parameters.empty?
notice 'Updated all hosts!'
redirect_to(hosts_path) and return
else
notice "#{counter} Parameters updated, see below for more information"
end
end

def select_multiple_hostgroup
end

def update_multiple_hostgroup
# simple validations
unless (id=params["hostgroup"]["id"])
error 'No Hostgroup selected!'
redirect_to(select_multiple_hostgroup_hosts_path) and return
end
unless (hg = Hostgroup.find id)
error 'Empty Hostgroup selected!'
redirect_to(select_multiple_hostgroup_hosts_path) and return
end

#update the hosts
@hosts.each do |host|
host.hostgroup=hg
host.save(false)
end

notice 'Updated hosts: Changed Hostgroup'
redirect_to(hosts_path)
end

def select_multiple_environment
end

def update_multiple_environment
# simple validations
if (params[:environment].nil?) or (id=params["environment"]["id"]).nil?
error 'No Environment selected!'
redirect_to(select_multiple_environment_hosts_path) and return
end
if (ev = Environment.find id).nil?
error 'Empty Environment selected!'
redirect_to(select_multiple_environment_hosts_path) and return
end

#update the hosts
@hosts.each do |host|
host.environment=ev
host.save(false)
end

notice 'Updated hosts: Changed Environment'
redirect_to(hosts_path)
end

def multiple_destroy
end

def multiple_build
end

def submit_multiple_build
@hosts.delete_if do |host|
host.request_url = request.host_with_port if host.respond_to?(:request_url)
host.setBuild
end

missed_hosts = @hosts.map(&:name).join('<br/>')
if @hosts.empty?
notice "The selected hosts will execute a build operation on next reboot"
else
error "The following hosts failed the build operation: #{missed_hosts}"
end
redirect_to(hosts_path)
end

def submit_multiple_destroy
# keep all the ones that were not deleted for notification.
@hosts.delete_if {|host| host.destroy}

missed_hosts = @hosts.map(&:name).join('<br/>')
if @hosts.empty?
notice "Destroyed selected hosts"
else
error "The following hosts were not deleted: #{missed_hosts}"
end
redirect_to(hosts_path)
end

def multiple_disable
end

def submit_multiple_disable
toggle_hostmode false
end

def multiple_enable
end

def submit_multiple_enable
toggle_hostmode
end

def errors
params[:search]="last_report > \"#{SETTINGS[:puppet_interval] + 5} minutes ago\" and (status.failed > 0 or status.failed_restarts > 0)"
index "Hosts with errors"
end

def active
params[:search]="last_report > \"#{SETTINGS[:puppet_interval] + 5} minutes ago\" and (status.applied > 0 or status.restarted > 0)"
index "Active Hosts"
end

def out_of_sync
params[:search]="last_report < \"#{SETTINGS[:puppet_interval]} minutes ago\" and status.enabled = true"
index "Hosts which didn't run puppet in the last #{SETTINGS[:puppet_interval]} minutes"
end

def disabled
params[:search]="status.enabled = false"
index "Hosts with notifications disabled"
end

def process_hostgroup
@hostgroup = Hostgroup.find(params[:hostgroup_id]) if params[:hostgroup_id].to_i > 0
return head(:not_found) unless @hostgroup

@architecture = @hostgroup.architecture
@operatingsystem = @hostgroup.operatingsystem
@environment = @hostgroup.environment

render :update do |page|
page['host_environment_id'].value = @hostgroup.environment_id if @hostgroup.environment_id
# process_hostgroup is only ever called for new host records therefore the assigned value can never be @hostgroup.puppetmaster_name
page['host_puppetproxy_id'].value = @hostgroup.puppetmaster.id if @hostgroup.puppetca?
if @environment
@host = Host.new
@host.hostgroup = @hostgroup
@host.environment = @environment
page.replace_html :classlist, :partial => 'puppetclasses/class_selection', :locals => {:obj => (@host)}
end

if (SETTINGS[:unattended].nil? or SETTINGS[:unattended])
page['host_root_pass'].value = @hostgroup.root_pass

if @architecture
page.replace_html :architecture_select, :partial => 'common/os_selection/architecture', :locals => {:item => @hostgroup}
page['host_architecture_id'].value = @architecture.id
end
if @operatingsystem
page['host_operatingsystem_id'].value = @operatingsystem.id
page.replace_html :operatingsystem_select, :partial => 'common/os_selection/operatingsystem', :locals => {:item => @hostgroup}
end
end
end
end

private
def find_hosts
fact, klass, group = params[:fact], params[:class], params[:hostgroup]

@verbose = params[:verbose] == "yes"

case params[:state]
when "out_of_sync"
state = "out_of_sync"
when "all"
state = "all"
when "active", nil
state = "recent"
else
raise invalid_request
end

@hosts = Host.send(state).map(&:name) if fact.empty? and klass.empty? and group.empty?
@hosts ||= []
counter = 0

# TODO: rewrite this part, my brain stopped working
# it should be possible for a one join
fact.each do |f|
# split facts based on name => value pairs
q = f.split("-seperator-")
invalid_request unless q.size == 2
list = Host.with_fact(*q).send(state).map(&:name)
@hosts = counter == 0 ? list : @hosts & list
counter +=1
end unless fact.nil?

klass.each do |k|
list = Host.with_class(k).send(state).map(&:name)
@hosts = counter == 0 ? list : @hosts & list
counter +=1
end unless klass.nil?

group.each do |k|
list = Host.hostgroup_name_eq(k).send(state).map(&:name)
@hosts = counter == 0 ? list : @hosts & list
counter +=1
end unless group.nil?

not_found if @hosts.empty?
end

def ajax_methods
return head(:method_not_allowed) unless request.xhr?
@host = Host.find(params[:id]) unless params[:id].empty?
end

def load_vars_for_ajax
return unless @host
@environment = @host.environment
@architecture = @host.architecture
@domain = @host.domain
@operatingsystem = @host.operatingsystem
end

def find_multiple
# Lets search by name or id and make sure one of them exists first
if params[:host_names].present? or params[:host_ids].present?
@hosts = Host.all(:conditions => ["id IN (?) or name IN (?)", params[:host_ids], params[:host_names] ])
if @hosts.empty?
error 'No hosts were found with that id or name'
redirect_to(hosts_path) and return false
end
else
error 'No Hosts selected'
redirect_to(hosts_path) and return false
end

rescue => e
error "Something went wrong while selecting hosts - #{e}"
redirect_to hosts_path
end

def toggle_hostmode mode=true
# keep all the ones that were not disabled for notification.
@hosts.delete_if { |host| host.update_attribute(:enabled, mode) }
action = mode ? "enabled" : "disabled"

missed_hosts = @hosts.map(&:name).join('<br/>')
if @hosts.empty?
notice "#{action.capitalize} selected hosts"
else
error "The following hosts were not #{action}: #{missed_hosts}"
end
redirect_to(hosts_path) and return
end

# Returns the associationes to include when doing a search.
# If the user has a fact_filter then we need to include :fact_values
# We do not include most associations unless we are processing a html page
def included_associations(include = [])
include += [:hostgroup, :domain, :operatingsystem, :environment, :model] unless request_json?
include += [:fact_values] if User.current.user_facts.any?
return include
end

# this is required for template generation (such as pxelinux) which is not done via a web request
def forward_request_url
@host.request_url = request.host_with_port if @host.respond_to?(:request_url)
end

def hypervisor_defaults msg = nil
@hypervisor = nil
render :update do |page|
page.alert(msg) if msg
page.replace_html :virtual_machine, :partial => "hypervisor"
# you can only select bare metal after you successfully selected a hypervisor before
page << "if (!$('host_mac')) {"
page.insert_html :after, :host_ip, :partial => "mac"
page[:host_hypervisor_id].value = ""
page << " }"
end
end

def disconnect_from_hypervisor
@hypervisor.disconnect if @hypervisor
end

end
(15-15/31)