Revision 43c4bd72
Added by Marek Hulán over 9 years ago
app/assets/javascripts/host_edit.js | ||
---|---|---|
$(document).on('AddedClass', function(event, link){load_puppet_class_parameters(link)});
|
||
$(document).on('click', '#params-tab', function() { resizeTextareas($('#params')); });
|
||
|
||
function update_nics(success_callback) {
|
||
var data = $('form').serialize().replace('method=put', 'method=post');
|
||
$('#network').html(spinner_placeholder(__('Loading interfaces information ...')));
|
||
$('#network_tab a').removeClass('tab-error');
|
||
|
||
var url = '/hosts/interfaces';
|
||
$.ajax({
|
||
type:'post',
|
||
url: url,
|
||
data: data,
|
||
complete: function(){},
|
||
error: function(jqXHR, status, error){
|
||
$('#network').html(Jed.sprintf(__("Error loading interfaces information: %s"), error));
|
||
$('#network_tab a').addClass('tab-error');
|
||
},
|
||
success: function(result){
|
||
$('#network').html(result);
|
||
if ($('#network').find('.alert-danger').length > 0)
|
||
$('#network_tab a').addClass('tab-error');
|
||
success_callback();
|
||
}
|
||
})
|
||
}
|
||
|
||
function computeResourceSelected(item){
|
||
var compute = $(item).val();
|
||
if (compute == '' && /compute_resource/.test($(item).attr('name'))) {
|
||
//Bare metal compute resource
|
||
$('#mac_address').show();
|
||
$("#model_name").show();
|
||
$('#compute_resource').empty();
|
||
$('#vm_details').empty();
|
||
... | ... | |
update_capabilities('build');
|
||
} else {
|
||
//Real compute resource or any compute profile
|
||
$('#mac_address').hide();
|
||
$("#model_name").hide();
|
||
$("#compute_resource_tab").show();
|
||
$("#compute_profile").show();
|
||
... | ... | |
}
|
||
})
|
||
}
|
||
update_nics(function() {
|
||
interface_subnet_selected(primary_nic_form().find('.interface_subnet'));
|
||
});
|
||
}
|
||
|
||
function update_capabilities(capabilities){
|
||
... | ... | |
success: function(response) {
|
||
$('form').replaceWith(response);
|
||
multiSelectOnLoad();
|
||
$("[id$='subnet_id']").first().change();
|
||
// to handle case if def process_taxonomy changed compute_resource_id to nil
|
||
if( !$('#host_compute_resource_id').val() ) {
|
||
$('#host_compute_resource_id').change();
|
||
}
|
||
update_capabilities($('#host_compute_resource_id').val() ? $('#capabilities').val() : 'build');
|
||
$(document.body).trigger('ContentLoad');
|
||
}
|
||
})
|
||
}
|
||
|
||
function subnet_selected(element){
|
||
var ipam_text = $("#host_ip").parentsUntil('.clearfix').find(".help-block,.help-inline");
|
||
if (selectedSubnetHasIPAM()) {
|
||
ipam_text.removeClass('hide')
|
||
} else {
|
||
ipam_text.addClass('hide');
|
||
return false
|
||
}
|
||
|
||
var subnet_id = $(element).val();
|
||
if (subnet_id == '' || $('#host_ip').length == 0) return;
|
||
|
||
// We do not query the proxy if the host_ip field is filled in and contains an
|
||
// IP that is in the selected subnet
|
||
var drop_text = $(element).children(":selected").text();
|
||
if (drop_text.length !=0 && drop_text.search(/^.+ \([0-9\.\/]+\)/) != -1) {
|
||
var details = drop_text.replace(/^.+\(/, "").replace(")","").split("/");
|
||
if (subnet_contains(details[0], details[1], $('#host_ip').val()))
|
||
return;
|
||
}
|
||
var attrs = attribute_hash(["subnet_id", "host_mac", 'organization_id', 'location_id']);
|
||
$(element).indicator_show();
|
||
var url = $(element).data('url');
|
||
$.ajax({
|
||
data: attrs,
|
||
type:'post',
|
||
url: url,
|
||
complete: function(){ $(element).indicator_hide();},
|
||
success: function(data){
|
||
$('#host_ip').val(data.ip);
|
||
$(document.body).trigger('ContentLoad');
|
||
}
|
||
})
|
||
}
|
||
... | ... | |
return integer;
|
||
}
|
||
|
||
function domain_selected(element){
|
||
var attrs = attribute_hash(['domain_id', 'organization_id', 'location_id']);
|
||
var url = $(element).data('url');
|
||
$(element).indicator_show();
|
||
$.ajax({
|
||
data: attrs,
|
||
type:'post',
|
||
url: url,
|
||
complete: function(){ $(element).indicator_hide();},
|
||
success: function(request) {
|
||
$('#subnet_select').html(request);
|
||
reload_host_params();
|
||
}
|
||
})
|
||
}
|
||
|
||
function architecture_selected(element){
|
||
var attrs = attribute_hash(['architecture_id', 'organization_id', 'location_id']);
|
||
var url = $(element).attr('data-url');
|
||
... | ... | |
interface_domain_selected(this);
|
||
});
|
||
|
||
$(document).on('click', '#suggest_new_ip', function (e) {
|
||
$('#host_ip').val('')
|
||
interface_subnet_selected($('#host_subnet_id'));
|
||
$(document).on('click', '.suggest_new_ip', function (e) {
|
||
$(this).closest('fieldset').find('.interface_ip').val('');
|
||
interface_subnet_selected($(this).closest('fieldset').find('.interface_subnet'));
|
||
e.preventDefault();
|
||
});
|
||
|
||
... | ... | |
subnet_options.append($("<option />").val(null).text(__('Please select')));
|
||
|
||
$.each(result, function () {
|
||
subnet_options.append($("<option />").val(this.subnet.id).text(this.subnet.name + ' (' + this.subnet.to_label + ')'));
|
||
subnet_options.append($("<option />").val(this.subnet.id).text(this.subnet.to_label));
|
||
});
|
||
if (subnet_options.find('option').length > 0) {
|
||
subnet_options.attr('disabled', false);
|
||
... | ... | |
return;
|
||
}
|
||
}
|
||
var interface_mac = $(element).parentsUntil('.fields').parent().find('input[id$=_mac]')
|
||
var interface_mac = $(element).closest('fieldset').find('input[id$=_mac]');
|
||
var url = $(element).attr('data-url');
|
||
var org = $('#host_organization_id :selected').val();
|
||
var loc = $('#host_location_id :selected').val();
|
||
|
||
var data = {subnet_id: subnet_id, host_mac: interface_mac.val(), organization_id:org, location_id:loc }
|
||
var taken_ips = $(active_interface_forms()).find('.interface_ip').map(function() {
|
||
return $(this).val();
|
||
}).get();
|
||
taken_ips.push(interface_ip.val());
|
||
|
||
var data = {
|
||
subnet_id: subnet_id,
|
||
host_mac: interface_mac.val(),
|
||
organization_id: org,
|
||
location_id: loc,
|
||
taken_ips: taken_ips
|
||
}
|
||
$.ajax({
|
||
data: data,
|
||
type:'post',
|
||
... | ... | |
dataType:'json',
|
||
success:function (result) {
|
||
interface_ip.val(result['ip']);
|
||
update_interface_table();
|
||
},
|
||
complete:function () {
|
||
$(element).indicator_hide();
|
||
... | ... | |
|
||
function interface_type_selected(element) {
|
||
var fieldset = $(element).closest("fieldset");
|
||
var data = fieldset.serializeArray();
|
||
data.push({
|
||
name: 'host[compute_resource_id]',
|
||
value: $('#host_compute_resource_id').val()
|
||
})
|
||
|
||
$.ajax({
|
||
data: fieldset.serialize(),
|
||
data: data,
|
||
type: 'GET',
|
||
url: fieldset.attr('data-url'),
|
||
dataType: 'script'
|
app/assets/javascripts/host_edit_interfaces.js | ||
---|---|---|
modal_window.find('.modal-body').append(modal_content.contents());
|
||
modal_window.find('.modal-title').html(__('Interface') + ' ' + String(identifier));
|
||
modal_window.modal({'show': true});
|
||
|
||
modal_window.find('a[rel="popover-modal"]').popover({html: true});
|
||
}
|
||
|
||
function close_interface_modal() {
|
||
function save_interface_modal() {
|
||
var modal_window = $('#interfaceModal');
|
||
var interface_id = modal_window.data('current-id');
|
||
|
||
var interface_row = get_interface_row(interface_id);
|
||
update_interface_row(interface_row, modal_window);
|
||
|
||
modal_window.modal('hide');
|
||
modal_window.removeData('current-id');
|
||
var modal_form = modal_window.find('.modal-body').contents();
|
||
if (modal_form.find('.interface_primary').is(':checked')) {
|
||
$('#interfaceForms .interface_primary:checked').attr("checked", false);
|
||
}
|
||
if (modal_form.find('.interface_provision').is(':checked')) {
|
||
$('#interfaceForms .interface_provision:checked').attr("checked", false);
|
||
}
|
||
|
||
var interface_hidden = get_interface_hidden(interface_id);
|
||
interface_hidden.html('');
|
||
interface_hidden.append(modal_window.find('.modal-body').contents());
|
||
interface_hidden.append(modal_form);
|
||
|
||
close_interface_modal();
|
||
sync_primary_name(false);
|
||
update_interface_table();
|
||
update_fqdn();
|
||
}
|
||
|
||
function sync_primary_name(ovewrite_blank) {
|
||
var nic_name = primary_nic_form().find('.interface_name');
|
||
var host_name = $('#host_name');
|
||
|
||
if (ovewrite_blank && (nic_name.val().length == 0))
|
||
nic_name.val(host_name.val());
|
||
else
|
||
host_name.val(nic_name.val());
|
||
}
|
||
|
||
function close_interface_modal() {
|
||
var modal_window = $('#interfaceModal');
|
||
|
||
modal_window.modal('hide');
|
||
modal_window.removeData('current-id');
|
||
modal_window.find('.modal-body').html('');
|
||
}
|
||
|
||
function get_interface_template_clone() {
|
||
var content = $('.interfaces_fields_template').html();
|
||
var content = $('#interfaces .interfaces_fields_template').html();
|
||
var interface_id = new Date().getTime();
|
||
|
||
content = fix_template_names(content, 'interfaces', interface_id);
|
||
... | ... | |
|
||
hidden.attr('id', 'interfaceHidden'+interface_id);
|
||
hidden.data('interface-id', interface_id);
|
||
hidden.find('.destroyFlag').val(0);
|
||
|
||
return hidden;
|
||
}
|
||
... | ... | |
if ( interface_row.length == 0) {
|
||
interface_row = $('#interfaceTemplate').clone(true);
|
||
interface_row.attr('id', 'interface'+interface_id);
|
||
interface_row.data('interface-id', interface_id);
|
||
|
||
interface_row.find('.showModal').click( function(){
|
||
edit_interface(interface_id);
|
||
... | ... | |
if ( interface_hidden.length == 0) {
|
||
|
||
interface_hidden = $('<div></div>');
|
||
interface_hidden.attr('style', 'display: none');
|
||
interface_hidden.attr('class', 'hidden');
|
||
interface_hidden.attr('id', 'interfaceHidden'+interface_id);
|
||
interface_hidden.data('interface-id', interface_id);
|
||
|
||
... | ... | |
return interface_hidden;
|
||
}
|
||
|
||
function update_interface_row(row, modal_window) {
|
||
row.find('.type').html(modal_window.find('.interface_type option:selected').text());
|
||
row.find('.identifier').html(modal_window.find('.interface_identifier').val());
|
||
row.find('.mac').html(modal_window.find('.interface_mac').val());
|
||
function fqdn(name, domain) {
|
||
if (!name || !domain)
|
||
return ""
|
||
else
|
||
return name + '.' + domain;
|
||
}
|
||
|
||
function update_interface_row(row, interface_form) {
|
||
row.find('.type').html(interface_form.find('.interface_type option:selected').text());
|
||
row.find('.identifier').html(interface_form.find('.interface_identifier').val());
|
||
row.find('.mac').html(interface_form.find('.interface_mac').val());
|
||
row.find('.ip').html(interface_form.find('.interface_ip').val());
|
||
|
||
var flags = '', primary_class = '', provision_class = '';
|
||
if (interface_form.find('.interface_primary').is(':checked'))
|
||
primary_class = 'active'
|
||
|
||
if (interface_form.find('.interface_provision').is(':checked'))
|
||
provision_class = 'active'
|
||
|
||
if (primary_class == '' && provision_class == '')
|
||
row.find('.removeInterface').removeClass('disabled');
|
||
else
|
||
row.find('.removeInterface').addClass('disabled');
|
||
|
||
flags += '<i class="glyphicon glyphicon glyphicon-tag primary-flag '+ primary_class +'" title="" data-original-title="'+ __('Primary') +'"></i>';
|
||
flags += '<i class="glyphicon glyphicon glyphicon-hdd provision-flag '+ provision_class +'" title="" data-original-title="'+ __('Provisioning') +'"></i>';
|
||
|
||
row.find('.flags').html(flags);
|
||
|
||
row.find('.fqdn').html(fqdn(
|
||
interface_form.find('.interface_name').val(),
|
||
interface_form.find('.interface_domain option:selected').text()
|
||
));
|
||
|
||
$('.primary-flag').tooltip();
|
||
$('.provision-flag').tooltip();
|
||
}
|
||
|
||
function update_interface_table() {
|
||
$.each(active_interface_forms(), function(index, form) {
|
||
var interface_id = $(form).data('interface-id');
|
||
|
||
var interface_row = get_interface_row(interface_id);
|
||
var interface_hidden = get_interface_hidden(interface_id)
|
||
|
||
update_interface_row(interface_row, interface_hidden);
|
||
})
|
||
}
|
||
|
||
function active_interface_forms() {
|
||
return $.grep($('#interfaceForms > div'), function(f) {
|
||
var flag = $(f).find('.destroyFlag').val();
|
||
return (flag == false || flag == undefined);
|
||
});
|
||
}
|
||
|
||
function confirm_flag_change(element, element_selector, massage) {
|
||
if (!$(element).is(':checked'))
|
||
return;
|
||
|
||
var this_interface_id = $('#interfaceModal').data('current-id');
|
||
|
||
var other_selected;
|
||
other_selected = $(active_interface_forms()).find(element_selector + ':checked').closest('fieldset');
|
||
other_selected = $.grep(other_selected, function(i) {
|
||
return ($(i).parent().data('interface-id') != this_interface_id);
|
||
});
|
||
|
||
if (other_selected.length > 0) {
|
||
return confirm(massage);
|
||
}
|
||
}
|
||
|
||
function primary_nic_form() {
|
||
return $(active_interface_forms()).find('.interface_primary:checked').closest('fieldset');
|
||
}
|
||
|
||
$(document).on('click', '.interface_primary', function () {
|
||
var confirmed = confirm_flag_change(this, '.interface_primary',
|
||
__("Some other interface is already set as primary. Are you sure you want to use this one instead?")
|
||
);
|
||
|
||
if (confirmed) {
|
||
// preset dns name from host name if it's blank
|
||
var name = $(this).closest('fieldset').find('.interface_name');
|
||
if (name.val().length == 0)
|
||
name.val($('#host_name').val());
|
||
}
|
||
|
||
return confirmed;
|
||
});
|
||
|
||
$(document).on('click', '.interface_provision', function () {
|
||
return confirm_flag_change(this, '.interface_provision',
|
||
__("Some other interface is already set as provisioning. Are you sure you want to use this one instead?")
|
||
);
|
||
});
|
||
|
||
$(document).on('change', '#host_name', function () {
|
||
// copy host name to the primary interface's name
|
||
primary_nic_form().find('.interface_name').val($(this).val());
|
||
update_interface_table();
|
||
update_fqdn();
|
||
});
|
||
|
||
$(document).on('click', '.primary-flag', function () {
|
||
var interface_id = $(this).closest('tr').data('interface-id');
|
||
|
||
$('#interfaceForms .interface_primary:checked').prop('checked', false);
|
||
get_interface_hidden(interface_id).find('.interface_primary').prop('checked', true);
|
||
|
||
sync_primary_name(true);
|
||
update_interface_table();
|
||
update_fqdn();
|
||
});
|
||
|
||
$(document).on('click', '.provision-flag', function () {
|
||
var interface_id = $(this).closest('tr').data('interface-id');
|
||
|
||
$('#interfaceForms .interface_provision:checked').prop('checked', false);
|
||
get_interface_hidden(interface_id).find('.interface_provision').prop('checked', true);
|
||
|
||
update_interface_table();
|
||
});
|
||
|
||
function update_fqdn() {
|
||
var host_name = $('#host_name').val();
|
||
var domain_name = primary_nic_form().find('.interface_domain option:selected').text();
|
||
|
||
var name = fqdn(host_name, domain_name)
|
||
if (name.length > 0)
|
||
name = "| " + name
|
||
|
||
$('#hostFQDN').text(name);
|
||
}
|
app/assets/stylesheets/application.scss | ||
---|---|---|
float: none;
|
||
}
|
||
|
||
#interfaceModal .modal-dialog {
|
||
min-width: 1000px
|
||
#interfaceModal {
|
||
.modal-dialog {
|
||
min-width: 1000px;
|
||
}
|
||
.modal-body {
|
||
max-height: none;
|
||
}
|
||
}
|
||
|
||
#addInterface {
|
||
... | ... | |
#interfaceList.table {
|
||
|
||
border-collapse: separate;
|
||
border-width: 0 0 1px 0;
|
||
border-width: 1px 0 1px 0;
|
||
|
||
tr {
|
||
th:first-child,
|
||
... | ... | |
border-bottom: 1px solid #a94442;
|
||
}
|
||
}
|
||
|
||
.primary-flag,
|
||
.provision-flag {
|
||
|
||
cursor: pointer;
|
||
color: #bbb;
|
||
margin: 2px;
|
||
|
||
&:hover {
|
||
color: #999;
|
||
}
|
||
|
||
&.active {
|
||
color: black;
|
||
&:hover {
|
||
color: #428bca;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
.autocomplete-input {
|
||
display:inline-block !important;
|
||
}
|
||
|
||
.glyphicon.nic-flag {
|
||
margin: 2px;
|
||
}
|
||
|
||
.lookup-keys-container{
|
||
li .close{
|
app/controllers/hosts_controller.rb | ||
---|---|---|
before_filter :ajax_request, :only => AJAX_REQUESTS
|
||
before_filter :find_resource, :only => [:show, :clone, :edit, :update, :destroy, :puppetrun, :review_before_build,
|
||
:setBuild, :cancelBuild, :power, :overview, :bmc, :vm,
|
||
:runtime, :resources, :templates, :ipmi_boot, :console,
|
||
:runtime, :resources, :templates, :nics, :ipmi_boot, :console,
|
||
:toggle_manage, :pxe_config, :storeconfig_klasses, :disassociate]
|
||
|
||
before_filter :taxonomy_scope, :only => [:new, :edit] + AJAX_REQUESTS
|
||
... | ... | |
load_vars_for_ajax
|
||
flash[:warning] = _("The marked fields will need reviewing")
|
||
@host.valid?
|
||
render :action => :new
|
||
end
|
||
|
||
def create
|
||
... | ... | |
end
|
||
end
|
||
|
||
def interfaces
|
||
@host = Host.new params[:host]
|
||
render :partial => "interfaces_tab"
|
||
end
|
||
|
||
def hostgroup_or_environment_selected
|
||
Taxonomy.as_taxonomy @organization, @location do
|
||
if params['host']['environment_id'].present? || params['host']['hostgroup_id'].present?
|
||
... | ... | |
process_ajax_error exception, 'fetch templates information'
|
||
end
|
||
|
||
def nics
|
||
render :partial => 'nics'
|
||
rescue ActionView::Template::Error => exception
|
||
process_ajax_error exception, 'fetch interfaces information'
|
||
end
|
||
|
||
def ipmi_boot
|
||
device = params[:ipmi_device]
|
||
device_id = BOOT_DEVICES.stringify_keys[device.downcase] || device
|
||
... | ... | |
|
||
def action_permission
|
||
case params[:action]
|
||
when 'clone', 'externalNodes', 'overview', 'bmc', 'vm', 'runtime', 'resources', 'templates',
|
||
when 'clone', 'externalNodes', 'overview', 'bmc', 'vm', 'runtime', 'resources', 'templates', 'nics',
|
||
'pxe_config', 'storeconfig_klasses', 'active', 'errors', 'out_of_sync', 'pending', 'disabled'
|
||
:view
|
||
when 'puppetrun', 'multiple_puppetrun', 'update_multiple_puppetrun'
|
app/controllers/interfaces_controller.rb | ||
---|---|---|
# {"new_1405068143746"=>
|
||
# {"_destroy"=>"false", "type"=>"Nic::BMC", "mac"=>"", "name"=>"", "domain_id"=>"", "ip"=>""}}}}
|
||
def new
|
||
@host = Host.new params[:host]
|
||
|
||
attributes = params[:host].fetch(:interfaces_attributes, {})
|
||
@key, attributes = attributes.first
|
||
raise Foreman::Exception, 'Missing attributes for interface' if @key.blank?
|
app/controllers/subnets_controller.rb | ||
---|---|---|
location = params[:location_id].blank? ? nil : Location.find(params[:location_id])
|
||
Taxonomy.as_taxonomy organization, location do
|
||
not_found and return unless (subnet = Subnet.authorized(:view_subnets).find(s))
|
||
if (ip = subnet.unused_ip(params[:host_mac]))
|
||
if (ip = subnet.unused_ip(params[:host_mac], params[:taken_ips]))
|
||
render :json => {:ip => ip}
|
||
else
|
||
# we don't want any failures if we failed to query our proxy
|
app/controllers/unattended_controller.rb | ||
---|---|---|
end
|
||
|
||
def find_host_by_spoof
|
||
host = Host.find_by_ip(params.delete('spoof')) if params['spoof'].present?
|
||
host = Nic::Base.primary.find_by_ip(params.delete('spoof')).try(:host) if params['spoof'].present?
|
||
host ||= Host.find(params.delete('hostname')) if params['hostname'].present?
|
||
@spoof = host.present?
|
||
host
|
||
... | ... | |
end
|
||
end
|
||
# we try to match first based on the MAC, falling back to the IP
|
||
Host.where(mac_list.empty? ? { :ip => ip } : ["lower(mac) IN (?)", mac_list]).first
|
||
# host is readonly because of association so we reload it if we find it
|
||
host = Host.joins(:primary_interface).where(mac_list.empty? ? {:nics => {:ip => ip}} : ["lower(nics.mac) IN (?)", mac_list]).first
|
||
host ? Host.find(host.id) : nil
|
||
end
|
||
|
||
def allowed_to_install?
|
app/helpers/hosts_and_hostgroups_helper.rb | ||
---|---|---|
:help_inline => _("Use this puppet server as an initial Puppet Server or to execute puppet runs") }
|
||
end
|
||
|
||
def realm_field(f)
|
||
# Don't show this if we have no Realms, otherwise always include blank
|
||
# so the user can choose not to use a Realm on this host
|
||
return if Realm.count == 0
|
||
return unless (SETTINGS[:unattended] == true) && @host.managed
|
||
select_f(f, :realm_id,
|
||
Realm.with_taxonomy_scope_override(@location, @organization).authorized(:view_realms),
|
||
:id, :to_label,
|
||
{ :include_blank => true },
|
||
{ :help_inline => :indicator }
|
||
).html_safe
|
||
end
|
||
|
||
def interesting_klasses(obj)
|
||
classes = obj.all_puppetclasses
|
||
smart_vars = LookupKey.reorder('').where(:puppetclass_id => classes.map(&:id)).group(:puppetclass_id).count
|
app/helpers/hosts_helper.rb | ||
---|---|---|
include ComputeResourcesVmsHelper
|
||
include BmcHelper
|
||
|
||
def nic_provider_attributes_exist?(host)
|
||
return false unless host.compute_resource
|
||
|
||
compute_resource_name = host.compute_resource.provider_friendly_name.downcase
|
||
real_path = File.join(Rails.root, 'app', 'views', 'compute_resources_vms', 'form', compute_resource_name, '_network.html.erb')
|
||
|
||
File.exist?(real_path)
|
||
end
|
||
|
||
def nic_provider_partial(host)
|
||
return nil unless host.compute_resource
|
||
|
||
compute_resource_name = host.compute_resource.provider_friendly_name.downcase
|
||
"compute_resources_vms/form/#{compute_resource_name}/network"
|
||
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)
|
||
... | ... | |
select_opts, html_opts
|
||
end
|
||
|
||
def new_host_title
|
||
t = _("New Host")
|
||
title(t, (t + ' <span id="hostFQDN"></span>').html_safe)
|
||
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? ? _("%s ago") % time_ago_in_words(record.last_report): ""
|
||
link_to_if_authorized(time,
|
||
... | ... | |
)
|
||
end
|
||
|
||
# we ignore interfaces.conflict because they are always registered in host errors as well
|
||
def conflict_objects(errors)
|
||
errors.keys.map(&:to_s).grep(/conflict$/).map(&:to_sym)
|
||
errors.keys.map(&:to_s).select { |key| key =~ /conflict$/ && key != 'interfaces.conflict' }.map(&:to_sym)
|
||
end
|
||
|
||
def has_conflicts?(errors)
|
||
... | ... | |
return '' if nic.new_record?
|
||
|
||
if nic.link
|
||
status = '<i class="glyphicon glyphicon glyphicon-arrow-up interface-up" title="'+ _('Up') +'"></i>'
|
||
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="'+ _('Down') +'"></i>'
|
||
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
|
app/models/compute_resource.rb | ||
---|---|---|
:image_id
|
||
end
|
||
|
||
def interfaces_attrs_name
|
||
"interfaces_attributes"
|
||
end
|
||
|
||
# returns a new fog server instance
|
||
def new_vm(attr = {})
|
||
test_connection
|
app/models/compute_resources/foreman/model/libvirt.rb | ||
---|---|---|
super.merge({:mac => :mac})
|
||
end
|
||
|
||
def interfaces_attrs_name
|
||
"nics_attributes"
|
||
end
|
||
|
||
def capabilities
|
||
[:build, :image]
|
||
end
|
app/models/compute_resources/foreman/model/ovirt.rb | ||
---|---|---|
end
|
||
|
||
def associated_host(vm)
|
||
Host.authorized(:view_hosts, Host).where(:mac => vm.interfaces.map { |i| i.mac }).first
|
||
Host.authorized(:view_hosts, Host).
|
||
joins(:primary_interface).
|
||
where(:nics => {:primary => true}).
|
||
where('nics.mac' => vm.interfaces.map { |i| i.mac }).
|
||
first
|
||
end
|
||
|
||
def self.provider_friendly_name
|
app/models/concerns/destroy_flag.rb | ||
---|---|---|
# DestroyFlag adds a flag and a corresponding reader method to any active record
|
||
# during deletion. The flag is set to true using before_destroy callback so during
|
||
# complicated association deletions you can check whether the deletion includes
|
||
# the object, e.g. we normally prevent deletion of primary interface in it's
|
||
# before filter but we have to allow it when we delete associated host.
|
||
#
|
||
# class Host
|
||
# include DestroyFlag
|
||
# end
|
||
#
|
||
# class Nic
|
||
# belongs_to :host
|
||
# before_destroy :keep_primary, :if => Proc.new { |nic| nic.primary? }
|
||
#
|
||
# def keep_primary
|
||
# unless host.being_destroyed? # Here we use being_destroyed? flag
|
||
# raise 'we can not delete primary'
|
||
# end
|
||
# end
|
||
# end
|
||
#
|
||
module DestroyFlag
|
||
extend ActiveSupport::Concern
|
||
|
||
def being_destroyed?
|
||
@_active_record_being_destroyed
|
||
end
|
||
|
||
included do
|
||
attr_accessor :_active_record_being_destroyed
|
||
before_destroy { |record| record._active_record_being_destroyed = true }
|
||
end
|
||
end
|
app/models/concerns/fog_extensions/libvirt/server.rb | ||
---|---|---|
_("%{cpus} CPUs and %{memory} memory") % {:cpus => cpus, :memory => number_to_human_size(memory.to_i)}
|
||
end
|
||
|
||
# Other Fog CRs use .interfaces as the accessor, but libvirt does not
|
||
def interfaces
|
||
nics
|
||
end
|
||
|
||
def select_nic(fog_nics, nic)
|
||
nic_attrs = nic.compute_attributes
|
||
match = fog_nics.detect { |fn| fn.network == nic_attrs['network'] } # grab any nic on the same network
|
||
match ||= fog_nics.detect { |fn| fn.bridge == nic_attrs['bridge'] } # no network? try a bridge...
|
||
match
|
||
end
|
||
|
||
end
|
||
end
|
||
end
|
app/models/concerns/fog_extensions/ovirt/server.rb | ||
---|---|---|
_("%{cores} Cores and %{memory} memory") % {:cores => cores, :memory => number_to_human_size(memory.to_i)}
|
||
end
|
||
|
||
def select_nic(fog_nics, nic)
|
||
fog_nics.detect {|fn| fn.network == nic.compute_attributes['network']} # grab any nic on the same network
|
||
end
|
||
|
||
end
|
||
end
|
||
end
|
app/models/concerns/fog_extensions/vsphere/server.rb | ||
---|---|---|
scsi_controller.type
|
||
end
|
||
|
||
def select_nic(fog_nics, nic)
|
||
fog_nics.detect {|fn| fn.network == nic.compute_attributes['network']} # grab any nic on the same network
|
||
end
|
||
|
||
end
|
||
end
|
||
end
|
app/models/concerns/host_common.rb | ||
---|---|---|
belongs_to :ptable
|
||
belongs_to :puppet_proxy, :class_name => "SmartProxy"
|
||
belongs_to :puppet_ca_proxy, :class_name => "SmartProxy"
|
||
belongs_to :domain, :counter_cache => counter_cache
|
||
belongs_to :realm, :counter_cache => counter_cache
|
||
belongs_to :subnet
|
||
belongs_to :compute_profile
|
||
|
||
before_save :check_puppet_ca_proxy_is_required?, :crypt_root_pass
|
app/models/concerns/host_template_helpers.rb | ||
---|---|---|
protocol = config.scheme || 'http'
|
||
port = config.port || request.port
|
||
host = config.host || request.host
|
||
path = config.path
|
||
|
||
@host ||= self
|
||
proxy = @host.try(:subnet).try(:tftp)
|
||
... | ... | |
host = uri.host
|
||
port = uri.port
|
||
protocol = uri.scheme
|
||
path = config.path
|
||
end
|
||
|
||
url_for :only_path => false, :controller => "/unattended", :action => action,
|
||
:protocol => protocol, :host => host, :port => port,
|
||
:protocol => protocol, :host => host, :port => port, :script_name => path,
|
||
:token => (@host.token.value unless @host.token.nil?)
|
||
end
|
||
|
app/models/concerns/hostext/search.rb | ||
---|---|---|
|
||
scoped_search :on => :name, :complete_value => true, :default_order => true
|
||
scoped_search :on => :last_report, :complete_value => true, :only_explicit => true
|
||
scoped_search :on => :ip, :complete_value => true
|
||
scoped_search :on => :comment, :complete_value => true
|
||
scoped_search :on => :enabled, :complete_value => {:true => true, :false => false}, :rename => :'status.enabled'
|
||
scoped_search :on => :managed, :complete_value => {:true => true, :false => false}
|
||
... | ... | |
scoped_search :in => :compute_resource, :on => :name, :complete_value => true, :rename => :compute_resource
|
||
scoped_search :in => :compute_resource, :on => :id, :complete_enabled => false, :rename => :compute_resource_id, :only_explicit => true
|
||
scoped_search :in => :image, :on => :name, :complete_value => true
|
||
|
||
scoped_search :in => :operatingsystem, :on => :name, :complete_value => true, :rename => :os
|
||
scoped_search :in => :operatingsystem, :on => :description, :complete_value => true, :rename => :os_description
|
||
scoped_search :in => :operatingsystem, :on => :title, :complete_value => true, :rename => :os_title
|
||
... | ... | |
scoped_search :in => :operatingsystem, :on => :minor, :complete_value => true, :rename => :os_minor
|
||
scoped_search :in => :operatingsystem, :on => :id, :complete_enabled => false,:rename => :os_id, :only_explicit => true
|
||
|
||
scoped_search :in => :primary_interface, :on => :ip, :complete_value => true
|
||
|
||
scoped_search :in => :puppetclasses, :on => :name, :complete_value => true, :rename => :class, :only_explicit => true, :operators => ['= ', '~ '], :ext_method => :search_by_puppetclass
|
||
scoped_search :in => :fact_values, :on => :value, :in_key=> :fact_names, :on_key=> :name, :rename => :facts, :complete_value => true, :only_explicit => true
|
||
scoped_search :in => :search_parameters, :on => :value, :on_key=> :name, :complete_value => true, :rename => :params, :ext_method => :search_by_params, :only_explicit => true
|
||
... | ... | |
if SETTINGS[:unattended]
|
||
scoped_search :in => :subnet, :on => :network, :complete_value => true, :rename => :subnet
|
||
scoped_search :in => :subnet, :on => :name, :complete_value => true, :rename => 'subnet.name'
|
||
scoped_search :on => :mac, :complete_value => true
|
||
scoped_search :on => :uuid, :complete_value => true
|
||
scoped_search :on => :build, :complete_value => {:true => true, :false => false}
|
||
scoped_search :on => :installed_at, :complete_value => true, :only_explicit => true
|
||
|
||
scoped_search :in => :provision_interface, :on => :mac, :complete_value => true
|
||
scoped_search :in => :operatingsystem, :on => :name, :complete_value => true, :rename => :os
|
||
scoped_search :in => :operatingsystem, :on => :description, :complete_value => true, :rename => :os_description
|
||
scoped_search :in => :operatingsystem, :on => :title, :complete_value => true, :rename => :os_title
|
||
scoped_search :in => :operatingsystem, :on => :major, :complete_value => true, :rename => :os_major
|
||
scoped_search :in => :operatingsystem, :on => :minor, :complete_value => true, :rename => :os_minor
|
||
scoped_search :in => :operatingsystem, :on => :id, :complete_value => false,:rename => :os_id, :complete_enabled => false
|
||
end
|
||
|
||
if SETTINGS[:login]
|
app/models/concerns/orchestration.rb | ||
---|---|---|
end
|
||
|
||
def on_destroy
|
||
errors.empty? ? process(:queue) : false
|
||
errors.empty? ? process(:queue) : rollback
|
||
end
|
||
|
||
def rollback
|
||
... | ... | |
update_cache
|
||
begin
|
||
task.status = execute({:action => task.action}) ? "completed" : "failed"
|
||
|
||
rescue Net::Conflict => e
|
||
task.status = "conflict"
|
||
record_conflicts << e
|
||
add_conflict(e)
|
||
failure e.message, nil, :conflict
|
||
rescue => e
|
||
task.status = "failed"
|
||
... | ... | |
rollback
|
||
end
|
||
|
||
def add_conflict(e)
|
||
@record_conflicts << e
|
||
end
|
||
|
||
def execute(opts = {})
|
||
obj, met = opts[:action]
|
||
rollback = opts[:rollback] || false
|
||
... | ... | |
end
|
||
|
||
# we keep the before update host object in order to compare changes
|
||
def setup_clone
|
||
def setup_clone(&block)
|
||
return if new_record?
|
||
@old = dup
|
||
for key in (changed_attributes.keys - ["updated_at"])
|
||
@old.send "#{key}=", changed_attributes[key]
|
||
# At this point the old cached bindings may still be present so we force an AR association reload
|
||
# This logic may not work or be required if we switch to Rails 3
|
||
if (match = key.match(/\A(.*)_id\Z/))
|
||
name = match[1].to_sym
|
||
next if name == :owner # This does not work for the owner association even from the console
|
||
self.send(name, true) if (send(name) and send(name).id != @attributes[key])
|
||
old.send(name, true) if (old.send(name) and old.send(name).id != old.attributes[key])
|
||
end
|
||
@old = setup_object_clone(self, &block)
|
||
end
|
||
|
||
def setup_object_clone(object)
|
||
clone = object.dup
|
||
yield(clone) if block_given?
|
||
# we can't assign using #attributes= because of mass-assign protected attributes (e.g. type)
|
||
for key in (object.changed_attributes.keys - ["updated_at"])
|
||
clone.send "#{key}=", object.changed_attributes[key]
|
||
end
|
||
clone
|
||
end
|
||
|
||
def orchestration_errors?
|
app/models/concerns/orchestration/compute.rb | ||
---|---|---|
|
||
def setCompute
|
||
logger.info "Adding Compute instance for #{name}"
|
||
add_interfaces_to_compute_attrs
|
||
self.vm = compute_resource.create_vm compute_attributes.merge(:name => Setting[:use_shortname_for_vms] ? shortname : name)
|
||
rescue => e
|
||
failure _("Failed to create a compute %{compute_resource} instance %{name}: %{message}\n ") % { :compute_resource => compute_resource, :name => name, :message => e.message }, e.backtrace
|
||
... | ... | |
def setComputeDetails
|
||
if vm
|
||
attrs = compute_resource.provided_attributes
|
||
normalize_addresses if attrs.keys.include?(:mac) or attrs.keys.include?(:ip)
|
||
|
||
attrs.each do |foreman_attr, fog_attr |
|
||
# we can't ensure uniqueness of #foreman_attr using normal rails validations as that gets in a later step in the process
|
||
# therefore we must validate its not used already in our db.
|
||
value = vm.send(fog_attr)
|
||
value ||= find_address if foreman_attr == :ip
|
||
self.send("#{foreman_attr}=", value)
|
||
|
||
if value.blank? or (other_host = Host.send("find_by_#{foreman_attr}", value))
|
||
delCompute
|
||
return failure("#{foreman_attr} #{value} is already used by #{other_host}") if other_host
|
||
return failure("#{foreman_attr} value is blank!")
|
||
if foreman_attr == :mac
|
||
#TODO, do we need handle :ip as well? for openstack / ec2 we only set a single
|
||
# interface (so host.ip will be fine), and we'd need to rethink #find_address :/
|
||
|
||
return false unless match_macs_to_nics(fog_attr)
|
||
else
|
||
value = vm.send(fog_attr)
|
||
value ||= find_address if foreman_attr == :ip
|
||
self.send("#{foreman_attr}=", value)
|
||
|
||
# validate_foreman_attr handles the failure msg, so we just bubble
|
||
# the false state up the stack
|
||
return false unless validate_foreman_attr(value,Host,foreman_attr)
|
||
end
|
||
end
|
||
true
|
||
... | ... | |
false
|
||
end
|
||
|
||
def add_interfaces_to_compute_attrs
|
||
# We now store vm fields in the Nic model, so we need to add them to
|
||
# compute_attrs before creating the vm
|
||
attrs_name = compute_resource.interfaces_attrs_name
|
||
return unless compute_attributes[attrs_name].blank?
|
||
compute_attributes[attrs_name] = {}
|
||
self.interfaces.each do |nic|
|
||
compute_attributes[attrs_name][nic.object_id.to_s] = nic.compute_attributes
|
||
end
|
||
end
|
||
|
||
def validate_foreman_attr(value,object,attr)
|
||
# we can't ensure uniqueness of #foreman_attr using normal rails
|
||
# validations as that gets in a later step in the process
|
||
# therefore we must validate its not used already in our db.
|
||
if value.blank?
|
||
delCompute
|
||
return failure("#{attr} value is blank!")
|
||
elsif (other_object = object.send("find_by_#{attr}", value))
|
||
delCompute
|
||
return failure("#{attr} #{value} is already used by #{other_object}")
|
||
end
|
||
true
|
||
end
|
||
|
||
def match_macs_to_nics(fog_attr)
|
||
# mac/ip are properties of the NIC, and there may be more than one,
|
||
# so we need to loop. First store the nics returned from Fog in a local
|
||
# array so we can delete from it safely
|
||
fog_nics = vm.interfaces.dup
|
||
|
||
self.interfaces.each do |nic|
|
||
selected_nic = vm.select_nic(fog_nics, nic)
|
||
next if selected_nic.nil? # found no matching fog nic for this Foreman nic, move on
|
||
|
||
mac = selected_nic.send(fog_attr)
|
||
logger.debug "Orchestration::Compute: nic #{nic.inspect} assigned to #{selected_nic.inspect}"
|
||
nic.mac = mac
|
||
fog_nics.delete(selected_nic) # don't use the same fog nic twice
|
||
|
||
# In future, we probably want to skip validation of macs/ips on the Nic
|
||
# macs can be duplicated if we are creating bonds
|
||
# ips can be duplicated if we have isolated subnets (needs an update in the Subnet model first)
|
||
# For now, we scope to physical devices only for the validations
|
||
|
||
# validate_foreman_attr handles the failure msg, so we just bubble
|
||
# the false state up the stack
|
||
return false unless validate_foreman_attr(mac,Nic::Base.physical,:mac)
|
||
end
|
||
true
|
||
end
|
||
end
|
app/models/concerns/orchestration/dhcp.rb | ||
---|---|---|
end
|
||
|
||
def dhcp?
|
||
hostname.present? && ip_available? && mac_available? && !subnet.nil? && subnet.dhcp? && managed?
|
||
hostname.present? && ip_available? && mac_available? && !subnet.nil? && subnet.dhcp? && host.managed? && managed?
|
||
end
|
||
|
||
def dhcp_record
|
app/models/concerns/orchestration/dns.rb | ||
---|---|---|
end
|
||
|
||
def dns?
|
||
hostname.present? and ip_available? and !domain.nil? and !domain.proxy.nil? and managed?
|
||
hostname.present? && ip_available? && !domain.nil? && !domain.proxy.nil? && host.managed? && managed?
|
||
end
|
||
|
||
def reverse_dns?
|
||
hostname.present? and ip_available? and !subnet.nil? and subnet.dns? and managed?
|
||
hostname.present? && ip_available? && !subnet.nil? && subnet.dns? && host.managed? && managed?
|
||
end
|
||
|
||
def dns_a_record
|
app/models/concerns/orchestration/tftp.rb | ||
---|---|---|
end
|
||
|
||
def tftp?
|
||
!!(subnet && subnet.tftp?) && (operatingsystem && operatingsystem.pxe_variant) && managed? && pxe_build?
|
||
provision? && !!(subnet && subnet.tftp?) && host.managed? && (host.operatingsystem && host.operatingsystem.pxe_variant) && managed? && pxe_build?
|
||
end
|
||
|
||
def tftp
|
||
subnet.tftp_proxy(:variant => operatingsystem.pxe_variant) if tftp?
|
||
subnet.tftp_proxy(:variant => host.operatingsystem.pxe_variant) if tftp?
|
||
end
|
||
|
||
protected
|
||
... | ... | |
# Adds the host to the forward and reverse TFTP zones
|
||
# +returns+ : Boolean true on success
|
||
def setTFTP
|
||
logger.info "Add the TFTP configuration for #{name}"
|
||
logger.info "Add the TFTP configuration for #{host.name}"
|
||
tftp.set mac, :pxeconfig => generate_pxe_template
|
||
end
|
||
|
||
# Removes the host from the forward and reverse TFTP zones
|
||
# +returns+ : Boolean true on success
|
||
def delTFTP
|
||
logger.info "Delete the TFTP configuration for #{name}"
|
||
logger.info "Delete the TFTP configuration for #{host.name}"
|
||
tftp.delete mac
|
||
end
|
||
|
||
def setTFTPBootFiles
|
||
logger.info "Fetching required TFTP boot files for #{name}"
|
||
logger.info "Fetching required TFTP boot files for #{host.name}"
|
||
valid = true
|
||
operatingsystem.pxe_files(medium, architecture, self).each do |bootfile_info|
|
||
host.operatingsystem.pxe_files(host.medium, host.architecture, self).each do |bootfile_info|
|
||
for prefix, path in bootfile_info do
|
||
valid = false unless tftp.fetch_boot_file(:prefix => prefix.to_s, :path => path)
|
||
end
|
||
... | ... | |
|
||
def validate_tftp
|
||
return unless tftp?
|
||
return unless operatingsystem
|
||
return unless host.operatingsystem
|
||
return if Rails.env == "test"
|
||
if configTemplate({:kind => operatingsystem.template_kind}).nil? and configTemplate({:kind => "iPXE"}).nil?
|
||
failure _("No %{template_kind} templates were found for this host, make sure you define at least one in your %{os} settings") % { :template_kind => operatingsystem.template_kind, :os => os }
|
||
if host.configTemplate({:kind => host.operatingsystem.template_kind}).nil? && host.configTemplate({:kind => "iPXE"}).nil?
|
||
failure _("No %{template_kind} templates were found for this host, make sure you define at least one in your %{os} settings") %
|
||
{ :template_kind => host.operatingsystem.template_kind, :os => host.os }
|
||
end
|
||
end
|
||
|
||
def generate_pxe_template
|
||
# this is the only place we generate a template not via a web request
|
||
# therefore some workaround is required to "render" the template.
|
||
|
||
@kernel = os.kernel(arch)
|
||
@initrd = os.initrd(arch)
|
||
@kernel = host.os.kernel(host.arch)
|
||
@initrd = host.os.initrd(host.arch)
|
||
# work around for ensuring that people can use @host as well, as tftp templates were usually confusing.
|
||
@host = self
|
||
@host = self.host
|
||
if build?
|
||
pxe_render configTemplate({:kind => os.template_kind})
|
||
pxe_render host.configTemplate({:kind => host.os.template_kind})
|
||
else
|
||
if os.template_kind == "PXEGrub"
|
||
if host.os.template_kind == "PXEGrub"
|
||
pxe_render ConfigTemplate.find_by_name("PXEGrub default local boot")
|
||
else
|
||
pxe_render ConfigTemplate.find_by_name("PXELinux default local boot")
|
||
end
|
||
end
|
||
rescue => e
|
||
failure _("Failed to generate %{template_kind} template: %{e}") % { :template_kind => os.template_kind, :e => e }
|
||
failure _("Failed to generate %{template_kind} template: %{e}") % { :template_kind => host.os.template_kind, :e => e }
|
||
end
|
||
|
||
def queue_tftp
|
||
return unless tftp? and errors.empty?
|
||
return unless tftp? && no_errors
|
||
# Jumpstart builds require only minimal tftp services. They do require a tftp object to query for the boot_server.
|
||
return true if jumpstart?
|
||
return true if host.jumpstart?
|
||
new_record? ? queue_tftp_create : queue_tftp_update
|
||
end
|
||
|
||
... | ... | |
def queue_tftp_update
|
||
set_tftp = false
|
||
# we switched build mode
|
||
set_tftp = true if old.build? != build?
|
||
set_tftp = true if old.host.build? != host.build?
|
||
# medium or arch changed
|
||
set_tftp = true if old.medium.try(:id) != medium.try(:id) or old.arch.try(:id) != arch.try(:id)
|
||
set_tftp = true if old.host.medium.try(:id) != host.medium.try(:id) or old.host.arch.try(:id) != host.arch.try(:id)
|
||
# operating system changed
|
||
set_tftp = true if os and old.os and (old.os.name != os.name or old.os.try(:id) != os.try(:id))
|
||
set_tftp = true if host.os and old.host.os and (old.host.os.name != host.os.name or old.host.os.try(:id) != host.os.try(:id))
|
||
# MAC address changed
|
||
if mac != old.mac
|
||
set_tftp = true
|
||
... | ... | |
end
|
||
|
||
def queue_tftp_destroy
|
||
return unless tftp? and errors.empty?
|
||
return true if jumpstart?
|
||
return unless tftp? && no_errors
|
||
return true if host.jumpstart?
|
||
queue.create(:name => _("TFTP Settings for %s") % self, :priority => 20,
|
||
:action => [self, :delTFTP])
|
||
end
|
||
|
||
def no_errors
|
||
errors.empty? && host.errors.empty?
|
||
end
|
||
|
||
end
|
app/models/domain.rb | ||
---|---|---|
audited :allow_mass_assignment => true
|
||
|
||
validates_lengths_from_database
|
||
has_many_hosts
|
||
has_many :hostgroups
|
||
#order matters! see https://github.com/rails/rails/issues/670
|
||
before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups, :subnets)
|
||
before_destroy EnsureNotUsedBy.new(:interfaces, :hostgroups, :subnets)
|
||
has_many :subnet_domains, :dependent => :destroy
|
||
has_many :subnets, :through => :subnet_domains
|
||
belongs_to :dns, :class_name => "SmartProxy"
|
||
has_many :domain_parameters, :dependent => :destroy, :foreign_key => :reference_id, :inverse_of => :domain
|
||
has_many :parameters, :dependent => :destroy, :foreign_key => :reference_id, :class_name => "DomainParameter"
|
||
has_many :interfaces, :class_name => 'Nic::Base'
|
||
has_many :primary_interfaces, :class_name => 'Nic::Base', :conditions => { :primary => true }
|
||
has_many :hosts, :through => :interfaces
|
||
has_many :primary_hosts, :through => :primary_interfaces, :source => :host
|
||
|
||
accepts_nested_attributes_for :domain_parameters, :allow_destroy => true
|
||
include ParameterValidators
|
||
... | ... | |
['name']
|
||
end
|
||
|
||
# overwrite method in taxonomix, since domain is not direct association of host anymore
|
||
def used_taxonomy_ids(type)
|
||
return [] if new_record?
|
||
Host::Base.joins(:primary_interface).where(:nics => {:domain_id => id}).pluck(type).compact.uniq
|
||
end
|
||
|
||
end
|
app/models/host/base.rb | ||
---|---|---|
include Authorizable
|
||
include CounterCacheFix
|
||
include Parameterizable::ByName
|
||
include DestroyFlag
|
||
|
||
self.table_name = :hosts
|
||
extend FriendlyId
|
||
... | ... | |
has_many :fact_names, :through => :fact_values
|
||
has_many :interfaces, :dependent => :destroy, :inverse_of => :host, :class_name => 'Nic::Base',
|
||
:foreign_key => :host_id, :order => 'identifier'
|
||
accepts_nested_attributes_for :interfaces, :reject_if => lambda { |a| a[:mac].blank? }, :allow_destroy => true
|
||
has_one :primary_interface, :class_name => 'Nic::Base', :foreign_key => 'host_id',
|
||
:conditions => { :primary => true }
|
||
has_one :provision_interface, :class_name => 'Nic::Base', :foreign_key => 'host_id',
|
||
:conditions => { :provision => true }
|
||
has_one :domain, :through => :primary_interface
|
||
has_one :subnet, :through => :primary_interface
|
||
accepts_nested_attributes_for :interfaces, :allow_destroy => true
|
||
|
||
alias_attribute :hostname, :name
|
||
before_validation :normalize_name
|
||
validates :name, :presence => true, :uniqueness => true, :format => {:with => Net::Validations::HOST_REGEXP}
|
||
validates :owner_type, :inclusion => { :in => OWNER_TYPES,
|
||
:allow_blank => true,
|
||
:message => (_("Owner type needs to be one of the following: %s") % OWNER_TYPES.join(', ')) }
|
||
validate :host_has_required_interfaces
|
||
|
||
# primary interface is mandatory because of delegated methods so we build it if it's missing
|
||
# similar for provision interface
|
||
# we can't set name attribute until we have primary interface so we don't pass it to super
|
||
# initializer and we set name when we are sure that we have primary interface
|
||
# we can't create primary interface before calling super because args may contain nested
|
||
# interface attributes
|
||
def initialize(*args)
|
||
primary_interface_attrs = [:name, :ip, :mac,
|
||
:subnet, :subnet_id, :subnet_name,
|
||
:domain, :domain_id, :domain_name,
|
||
:lookup_values_attributes]
|
||
values_for_primary_interface = {}
|
||
|
||
new_attrs = args.shift
|
||
unless new_attrs.nil?
|
||
new_attrs = new_attrs.with_indifferent_access
|
||
primary_interface_attrs.each do |attr|
|
||
values_for_primary_interface[attr] = new_attrs.delete(attr) if new_attrs.has_key?(attr)
|
||
end
|
||
args.unshift(new_attrs.to_hash)
|
||
end
|
||
|
||
super(*args)
|
||
|
||
self.interfaces.build(:primary => true, :type => 'Nic::Managed') if self.primary_interface.nil?
|
||
self.primary_interface.provision = true if self.provision_interface.nil?
|
||
values_for_primary_interface.each do |name, value|
|
||
self.send "#{name}=", value
|
||
end
|
||
end
|
||
|
||
|
||
delegate :ip, :mac,
|
||
:subnet, :subnet_id, :subnet_name,
|
||
:domain, :domain_id, :domain_name,
|
||
:hostname,
|
||
:to => :primary_interface, :allow_nil => true
|
||
delegate :name=, :ip=, :mac=, :subnet=, :subnet_id=, :subnet_name=,
|
||
:domain=, :domain_id=, :domain_name=, :to => :primary_interface
|
||
|
||
attr_writer :updated_virtuals
|
||
def updated_virtuals
|
||
... | ... | |
end
|
||
|
||
def set_interfaces(parser)
|
||
# if host has no information in primary interface we try to match it and update it
|
||
# instead of creating new interface, suggested primary interface mac and identifier
|
||
# is saved to primary interface so we match it in updating code below
|
||
if !self.managed? && self.primary_interface.mac.blank? && self.primary_interface.identifier.blank?
|
||
identifier, values = parser.suggested_primary_interface(self)
|
||
self.primary_interface.mac = Net::Validations.normalize_mac(values[:macaddress])
|
||
self.primary_interface.identifier = identifier
|
||
self.primary_interface.save!
|
||
end
|
||
|
||
parser.interfaces.each do |name, attributes|
|
||
begin
|
||
macaddress = Net::Validations.normalize_mac(attributes[:macaddress])
|
||
... | ... | |
# if we want to update the device it must have same identifier
|
||
base = base.virtual.where(:identifier => name)
|
||
else
|
||
# for physical devices we ignore primary interface which is updated by other facts
|
||
# we just update its name and log it
|
||
if macaddress != Net::Validations.normalize_mac(self.mac)
|
||
base = base.physical
|
||
else
|
||
logger.debug "Skipping #{name} since it is primary interface of host #{self.name}"
|
||
old = self.primary_interface
|
||
self.update_attribute :primary_interface, name
|
||
update_virtuals(old, name) if old != name && old.present?
|
||
next
|
||
end
|
||
base = base.physical
|
||
end
|
||
|
||
iface = base.first || interface_class(name).new(:managed => false)
|
||
... | ... | |
iface.provider ||= 'IPMI'
|
||
set_interface(ipmi, 'ipmi', iface)
|
||
end
|
||
|
||
self.interfaces.reload
|
||
end
|
||
|
||
def facts_hash
|
||
... | ... | |
comparison_object.id == id
|
||
end
|
||
|
||
def normalize_name
|
||
self.name = Net::Validations.normalize_hostname(name) if self.name.present?
|
||
end
|
||
|
||
def set_taxonomies(facts)
|
||
['location', 'organization'].each do |taxonomy|
|
||
next unless SETTINGS["#{taxonomy.pluralize}_enabled".to_sym]
|
||
... | ... | |
@overwrite = value.to_s == "true"
|
||
end
|
||
|
||
def has_primary_interface?
|
||
self.primary_interface.present?
|
||
def primary_interface
|
||
get_interface_by_flag(:primary)
|
||
end
|
||
|
||
def provision_interface
|
||
get_interface_by_flag(:provision)
|
||
end
|
||
|
||
def managed_interfaces
|
||
... | ... | |
self.interfaces.is_managed.where(:identifier => identifiers).all
|
||
end
|
||
|
||
def reload(*args)
|
||
drop_primary_interface_cache
|
||
drop_provision_interface_cache
|
||
super
|
Also available in: Unified diff
Fixes #7456 - Extract primary interface from host
Contributions from:All host must have at least one primary interface and one provision (can
be the same interface). Primary interface gives host a name so even
unamanaged host have primary interface (we skip validations of other
attributes for unamanged hosts though).
Host still have name attribute which is a cache of primary interface name.
Therefore we can use the host name in SQL queries, as a friendly_id etc.
- realm moved to the primary tab
- fqdn in nics table
- flags in nics table
- checkboxes for provision and primary flags
- modal resize fix
- original fields for primary NIC removed
- skipping validation for new resources
- warnings before switching flags and table update
- host name and primary interface name connected
- host domain name in the page title
- nics on host show page
- clearing modal window on cancel
- fixed domain validation in NIC::Base
- ip suggestion for all interfaces
- flags switchable from the overview table
- use icons instead of text for primary/provision on NICs overview tab
- attempt to fix sending NIC template
- fix fqdn algorithm
- ip addres in the overview table
- fix for class name collision
- better behavior of host name <-> primary name sync
- fix for subnet combobox values
- fix for modal poping up on form submission
- network partial for CRs moved from VM tab to modal
- fix ip suggestion race for ipam=db