Mohammed Morsi
2008-Oct-31 01:27 UTC
[Ovirt-devel] [PATCH server] network integration into ovirt server db and wui
--- src/app/controllers/host_controller.rb | 12 +- src/app/controllers/network_controller.rb | 427 ++++++++++++++++++++ src/app/helpers/application_helper.rb | 28 +- src/app/helpers/network_helper.rb | 2 + src/app/models/bonding.rb | 27 +- src/app/models/ip_address.rb | 27 ++ src/app/models/ip_v4_address.rb | 48 +++ src/app/models/ip_v6_address.rb | 44 ++ src/app/models/network.rb | 37 ++ src/app/models/nic.rb | 16 +- src/app/models/physical_network.rb | 27 ++ src/app/models/usage.rb | 21 + src/app/models/vlan.rb | 30 ++ src/app/views/dashboard/index.html.erb | 7 + src/app/views/host/edit_network.rhtml | 85 ++++ src/app/views/host/show.rhtml | 4 + src/app/views/network/_bonding_form.rhtml | 46 +++ src/app/views/network/_form.rhtml | 31 ++ src/app/views/network/_grid.rhtml | 37 ++ src/app/views/network/_ip_address_form.rhtml | 62 +++ src/app/views/network/_ip_addresses_form.rhtml | 50 +++ src/app/views/network/_select.rhtml | 32 ++ src/app/views/network/edit.rhtml | 34 ++ src/app/views/network/edit_bonding.rhtml | 52 +++ src/app/views/network/edit_ip_address.rhtml | 66 +++ .../views/network/edit_network_ip_addresses.rhtml | 13 + src/app/views/network/edit_nic.rhtml | 62 +++ src/app/views/network/list.html.erb | 72 ++++ src/app/views/network/new.rhtml | 28 ++ src/app/views/network/new_bonding.rhtml | 32 ++ src/app/views/network/new_ip_address.rhtml | 33 ++ src/app/views/network/show.rhtml | 30 ++ src/app/views/nic/_list.rhtml | 2 - src/db/migrate/028_refactor_networking_model.rb | 271 +++++++++++++ src/public/javascripts/ovirt.js | 29 ++- src/public/stylesheets/components.css | 50 +++- 36 files changed, 1849 insertions(+), 25 deletions(-) create mode 100644 src/app/controllers/network_controller.rb create mode 100644 src/app/helpers/network_helper.rb create mode 100644 src/app/models/ip_address.rb create mode 100644 src/app/models/ip_v4_address.rb create mode 100644 src/app/models/ip_v6_address.rb create mode 100644 src/app/models/network.rb create mode 100644 src/app/models/physical_network.rb create mode 100644 src/app/models/usage.rb create mode 100644 src/app/models/vlan.rb create mode 100644 src/app/views/host/edit_network.rhtml create mode 100644 src/app/views/network/_bonding_form.rhtml create mode 100644 src/app/views/network/_form.rhtml create mode 100644 src/app/views/network/_grid.rhtml create mode 100644 src/app/views/network/_ip_address_form.rhtml create mode 100644 src/app/views/network/_ip_addresses_form.rhtml create mode 100644 src/app/views/network/_select.rhtml create mode 100644 src/app/views/network/edit.rhtml create mode 100644 src/app/views/network/edit_bonding.rhtml create mode 100644 src/app/views/network/edit_ip_address.rhtml create mode 100644 src/app/views/network/edit_network_ip_addresses.rhtml create mode 100644 src/app/views/network/edit_nic.rhtml create mode 100644 src/app/views/network/list.html.erb create mode 100644 src/app/views/network/new.rhtml create mode 100644 src/app/views/network/new_bonding.rhtml create mode 100644 src/app/views/network/new_ip_address.rhtml create mode 100644 src/app/views/network/show.rhtml create mode 100644 src/db/migrate/028_refactor_networking_model.rb diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb index a40d297..120fe2a 100644 --- a/src/app/controllers/host_controller.rb +++ b/src/app/controllers/host_controller.rb @@ -30,7 +30,7 @@ class HostController < ApplicationController end end - before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms] + before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms, :edit_network] # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => [:post, :put], :only => [ :create, :update ], @@ -156,6 +156,16 @@ class HostController < ApplicationController render :json => @json_hash end + def edit_network + render :layout => 'popup' + end + + def bondings_json + bondings = Host.find(params[:id]).bondings + bondings_json = [] + bondings.each{ |x| bondings_json.push({:id => x.id, :name => x.name}) } + render :json => bondings_json + end private #filter methods diff --git a/src/app/controllers/network_controller.rb b/src/app/controllers/network_controller.rb new file mode 100644 index 0000000..b7f8b6d --- /dev/null +++ b/src/app/controllers/network_controller.rb @@ -0,0 +1,427 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. +# + +class NetworkController < ApplicationController + ########################## Networks related actions + + def network_permissions + # TODO more robust permission system + # either by subclassing network from pool + # or by extending permission model to accomodate + # any object + @default_pool = HardwarePool.get_default_pool + set_perms(@default_pool) + unless @can_modify + flash[:notice] = 'You do not have permission to view networks' + redirect_to :controller => 'dashboard' + end + end + + def list + @networks = Network.find(:all) + network_permissions + end + + def networks_json + json_list(Network.find(:all), [:id, :name, :type, [:boot_type, :label]]) + end + + def show + @network = Network.find(params[:id]) + network_permissions + respond_to do |format| + format.html { render :layout => 'selection' } + format.xml { render :xml => @network.to_xml } + end + end + + def new + @boot_types = BootType.find(:all) + render :layout => 'popup' + end + + def create + begin + @network = PhysicalNetwork.new(params[:network]) if params[:network][:type] == 'PhysicalNetwork' + @network = Vlan.new(params[:network]) if params[:network][:type] == 'Vlan' + @network.save! + alert = "Network was successfully created." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue + render :json => { :object => "network", :success => false, + :errors => + @network.errors.localize_error_messages.to_a } + end + end + + def edit + @network = Network.find(params[:id]) + @boot_types = BootType.find(:all) + render :layout => 'popup' + end + + def update + begin + @network = Network.find(params[:id]) + + # special case if we are switching types + if @network.type != params[:network][:type] + if ! @network.conversion_valid? + render :json => { :object => "network", :success => false, + :alert => + 'Can not change type of network with associated nics/bondings'} + return + end + + if params[:network][:type] == 'PhysicalNetwork' + Vlan.destroy(params[:id]) + @network = PhysicalNetwork.create(params[:network]) + else + PhysicalNetwork.destroy(params[:id]) + @network = Vlan.create(params[:network]) + end + else + @network = PhysicalNetwork.find(params[:id]) if @network.type == 'PhysicalNetwork' + @network = Vlan.find(params[:id]) if @network.type == 'Vlan' + @network.update_attributes!(params[:network]) + end + + alert = "Network was successfully updated." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue Exception => e + render :json => { :object => "network", :success => false, + :errors => + @network.errors.localize_error_messages.to_a } + end + end + + def delete + failed_networks = [] + networks_ids_str = params[:network_ids] + network_ids = networks_ids_str.split(",").collect {|x| x.to_i} + network_ids.each{ |x| + network = Network.find(x) + if network.conversion_valid? + begin + Network.destroy(x) + rescue + failed_networks.push x + end + else + failed_networks.push x + end + } + if failed_networks.size == 0 + render :json => { :object => "network", + :success => true, + :alert => "Successfully deleted networks" } + else + render :json => { :object => "network", + :success => false, + :alert => "Failed deleting " + + failed_networks.size.to_s + + " networks due to existing bondings/nics" } + end + end + + def edit_network_ip_addresses + @network = Network.find(params[:id]) + render :layout => 'popup' + end + + + ########################## Ip Address related actions + + def ip_addresses_json + @parent_type = params[:parent_type] + if @parent_type == 'network' + ip_addresses = Network.find(params[:id]).ip_addresses + elsif @parent_type == 'nic' + ip_addresses = Nic.find(params[:id]).ip_addresses + elsif @parent_type == 'bonding' and params[:id] + ip_addresses = Bonding.find(params[:id]).ip_addresses + else + ip_addresses = [] + end + + ip_addresses_json = [] + ip_addresses.each{ |x| + ip_addresses_json.push({:id => x.id, :name => x.address}) } + render :json => ip_addresses_json + end + + def new_ip_address + @parent_type = params[:parent_type] + @network = Network.find(params[:id]) if @parent_type == 'network' + @nic = Nic.find(params[:id]) if @parent_type == 'nic' + @bonding = Bonding.find(params[:id]) if @parent_type == 'bonding' and params[:id] + + render :layout => false + end + + def _create_ip_address + if params[:ip_address][:type] == "IpV4Address" + @ip_address = IpV4Address.new(params[:ip_address]) + else + @ip_address = IpV6Address.new(params[:ip_address]) + end + @ip_address.save! + end + + def create_ip_address + begin + _create_ip_address + alert = "Ip Address was successfully created." + render :json => { :object => "ip_address", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a } + end + end + + def edit_ip_address + @ip_address = IpAddress.find(params[:id]) + + @parent_type = params[:parent_type] + @network = @ip_address.network if @ip_address.network_id + @nic = @ip_address.nic if @ip_address.nic_id + @bonding = @ip_address.bonding if @ip_address.bonding_id + + render :layout => false + end + + def _update_ip_address(id) + @ip_address = IpAddress.find(id) + + # special case if we are switching types + if @ip_address.type != params[:ip_address][:type] + if params[:ip_address][:type] == 'IpV4Address' + @ip_address = IpV4Address.new(params[:ip_address]) + @ip_address.save! + IpV6Address.delete(id) + else + @ip_address = IpV6Address.new(params[:ip_address]) + @ip_address.save! + IpV4Address.delete(id) + end + else + if @ip_address.type == 'IpV4Address' + @ip_address = IpV4Address.find(id) + else + @ip_address = IpV6Address.find(id) + end + @ip_address.update_attributes!(params[:ip_address]) + end + + end + + def update_ip_address + begin + _update_ip_address(params[:id]) + alert = "IpAddress was successfully updated." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a } + end + end + + def destroy_ip_address + begin + IpAddress.delete(params[:id]) + alert = "Ip Address was successfully deleted." + render :json => { :object => "ip_address", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :alert => 'Ip Address Deletion Failed' } + end + end + + + ########################## NICs related actions + + def edit_nic + @nic = Nic.find(params[:id]) + @network = @nic.physical_network + + @networks = PhysicalNetwork.find(:all) + network_options + + render :layout => false + end + + def update_nic + begin + network_options + @network = Network.find(params[:nic][:physical_network_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @nic = Nic.find(params[:id]) + @nic.update_attributes!(params[:nic]) + + alert = "Nic was successfully updated." + render :json => { :object => "nic", :success => true, + :alert => alert } + rescue Exception => e + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "nic", :success => false, + :errors => + @nic.errors.localize_error_messages.to_a } + end + end + end + + ########################## Bonding related actions + + def new_bonding + unless params[:host_id] + flash[:notice] = "Host is required." + redirect_to :controller => 'dashboard' + end + + @host = Host.find(params[:host_id]) + @networks = Vlan.find(:all) + network_options + + render :layout => false + end + + def create_bonding + begin + network_options + @network = Network.find(params[:bonding][:vlan_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @bonding = Bonding.new(params[:bonding]) + @bonding.save! + + if @ip_address + @ip_address.bonding_id = @bonding.id + @ip_address.save! + end + + alert = "Bonding was successfully created." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "bonding", :success => false, + :errors => + @bonding.errors.localize_error_messages.to_a } + end + end + end + + def edit_bonding + @bonding = Bonding.find(params[:id]) + @network = @bonding.vlan + + @host = @bonding.host + @networks = Vlan.find(:all) + network_options + + render :layout => false + end + + def update_bonding + begin + network_options + @network = Network.find(params[:bonding][:vlan_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @bonding = Bonding.find(params[:id]) + @bonding.nics.each { |nic| @bonding.nics.delete(nic) } + @bonding.update_attributes!(params[:bonding]) + + alert = "Bonding was successfully updated." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "bonding", :success => false, + :errors => + @bonding.errors.localize_error_messages.to_a } + end + end + end + + def destroy_bonding + begin + Bonding.destroy(params[:id]) + alert = "Bonding was successfully deleted." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + render :json => { :object => "bonding", :success => false, + :alert => 'Bonding Deletion Failed' } + end + + end + + + ########################## Misc methods + + protected + def network_options + @bonding_types = BondingType.find(:all) + @static_boot_type = BootType.find(:first, + :conditions => { :proto => 'static' }) + end + +end diff --git a/src/app/helpers/application_helper.rb b/src/app/helpers/application_helper.rb index d7b6628..0178ad0 100644 --- a/src/app/helpers/application_helper.rb +++ b/src/app/helpers/application_helper.rb @@ -84,21 +84,31 @@ module ApplicationHelper } end - def popup_footer(action, label) - %{ - <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0;"> + # expects hash of labels => actions + def multi_button_popup_footer(buttons = {}) + buttons_html = "" + buttons.each{ |label, action| + buttons_html+="<div class=\"button\">" + + " <div class=\"button_left_blue\"></div>" + + " <div class=\"button_middle_blue\"><a href=\"#\" onclick=\"#{action}\">#{label}</a></div>" + + " <div class=\"button_right_blue\"></div>" + + "</div>" + } + + %{ + <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0; float: left; width: 97%;"> <div class="button"> <div class="button_left_grey"></div> <div class="button_middle_grey"><a href="#" onclick="$(document).trigger('close.facebox')">Cancel</a></div> <div class="button_right_grey"></div> </div> - <div class="button"> - <div class="button_left_blue"></div> - <div class="button_middle_blue"><a href="#" onclick="#{action}">#{label}</a></div> - <div class="button_right_blue"></div> - </div> + #{buttons_html} </div> - } + } + end + + def popup_footer(action, label) + multi_button_popup_footer({label => action}) end def ok_footer diff --git a/src/app/helpers/network_helper.rb b/src/app/helpers/network_helper.rb new file mode 100644 index 0000000..ebbce0b --- /dev/null +++ b/src/app/helpers/network_helper.rb @@ -0,0 +1,2 @@ +module NetworkHelper +end diff --git a/src/app/models/bonding.rb b/src/app/models/bonding.rb index 006c261..a3ad150 100644 --- a/src/app/models/bonding.rb +++ b/src/app/models/bonding.rb @@ -30,6 +30,16 @@ # interface. They can be ignored if not used. # class Bonding < ActiveRecord::Base + belongs_to :host + belongs_to :bonding_type + belongs_to :vlan + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :nics, + :join_table => 'bondings_nics', + :foreign_key => :bonding_id + + validates_presence_of :name, :message => 'A name is required.' @@ -42,18 +52,17 @@ class Bonding < ActiveRecord::Base validates_presence_of :interface_name, :message => 'An interface name is required.' - validates_presence_of :boot_type_id, - :message => 'A boot type must be specified.' - validates_presence_of :bonding_type_id, :message => 'A bonding type must be specified.' - belongs_to :host - belongs_to :bonding_type - belongs_to :boot_type + protected + def validate + errors.add("name", "must be specified") unless name + errors.add("interface_name", "must be specified") unless interface_name + errors.add("bonding_type_id", "must be specified") unless bonding_type_id + errors.add("host_id", "must be specified") unless host_id + errors.add("vlan_id", "must be specified") unless vlan_id + end - has_and_belongs_to_many :nics, - :join_table => 'bondings_nics', - :foreign_key => :bonding_id end diff --git a/src/app/models/ip_address.rb b/src/app/models/ip_address.rb new file mode 100644 index 0000000..5d2e6af --- /dev/null +++ b/src/app/models/ip_address.rb @@ -0,0 +1,27 @@ +# ip_address.rb +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpAddress+ is the base class for all address related classes. +# +class IpAddress < ActiveRecord::Base + # one of these 3 will apply for each address + belongs_to :network + belongs_to :nic + belongs_to :bonding +end diff --git a/src/app/models/ip_v4_address.rb b/src/app/models/ip_v4_address.rb new file mode 100644 index 0000000..40e8cf9 --- /dev/null +++ b/src/app/models/ip_v4_address.rb @@ -0,0 +1,48 @@ +# ip_v4_address.rb +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com>, +# Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpV4Address+ represents a single IPv4 address. +# +class IpV4Address < IpAddress + ADDRESS_TEST = %r{^(\d{1,3}\.){3}\d{1,3}$} + + validates_presence_of :address, + :message => 'An address must be supplied.' + validates_format_of :address, + :with => ADDRESS_TEST + + protected + def validate + unless address and address =~ ADDRESS_TEST + errors.add("address", "is of incorrect format") + end + unless !netmask or netmask == "" or netmask =~ ADDRESS_TEST + errors.add("netmask", "is of incorrect format") + end + unless !gateway or gateway == "" or gateway =~ ADDRESS_TEST + errors.add("gateway", "is of incorrect format") + end + unless !broadcast or broadcast == "" or broadcast =~ ADDRESS_TEST + errors.add("broadcast", "is of incorrect format") + end + end + +end diff --git a/src/app/models/ip_v6_address.rb b/src/app/models/ip_v6_address.rb new file mode 100644 index 0000000..9720179 --- /dev/null +++ b/src/app/models/ip_v6_address.rb @@ -0,0 +1,44 @@ +# ip_v6_address.rb +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com>, +# Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpV6Address+ represents a single IPv6 address. +# +class IpV6Address < IpAddress + ADDRESS_TEST = %r{^([0-9a-fA-F]{0,4}|0)(\:([0-9a-fA-F]{0,4}|0)){7}$} + + validates_presence_of :address, + :message => 'An address must be provided.' + validates_format_of :address, + :with => ADDRESS_TEST + + protected + def validate + unless address =~ ADDRESS_TEST + errors.add("address", "is of incorrect format") + end + unless !prefix or prefix == "" or prefix =~ ADDRESS_TEST + errors.add("prefix", "is of incorrect format") + end + unless !gateway or gateway == "" or gateway =~ ADDRESS_TEST + errors.add("gateway", "is of incorrect format") + end + end +end diff --git a/src/app/models/network.rb b/src/app/models/network.rb new file mode 100644 index 0000000..99e1a94 --- /dev/null +++ b/src/app/models/network.rb @@ -0,0 +1,37 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Network < ActiveRecord::Base + belongs_to :boot_type + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :usages, :join_table => 'networks_usages' + + validates_presence_of :type + validates_presence_of :name + validates_presence_of :boot_type_id + + def conversion_valid? + return false if type.to_s == 'Vlan' && + Vlan.find(id).bondings.size != 0 || + type.to_s == 'PhysicalNetwork' && + PhysicalNetwork.find(id).nics.size != 0 + return true + end + +end diff --git a/src/app/models/nic.rb b/src/app/models/nic.rb index baf7095..25d3ac2 100644 --- a/src/app/models/nic.rb +++ b/src/app/models/nic.rb @@ -19,7 +19,19 @@ class Nic < ActiveRecord::Base belongs_to :host - belongs_to :boot_type + belongs_to :physical_network + has_many :ip_addresses, :dependent => :destroy - has_and_belongs_to_many :bonding, :join_table => 'bondings_nics' + has_and_belongs_to_many :bondings, :join_table => 'bondings_nics' + + protected + def validate + errors.add("host_id", "must be specified") unless host_id + errors.add("physical_network_id", "must be specified") unless physical_network_id + if physical_network.boot_type.id =+ BootType.find(:first, :conditions => { :proto => 'static' }).id and + ip_addresses.size == 0 + errors.add("ip_address_id", "must be specified") + end + end end diff --git a/src/app/models/physical_network.rb b/src/app/models/physical_network.rb new file mode 100644 index 0000000..cba696a --- /dev/null +++ b/src/app/models/physical_network.rb @@ -0,0 +1,27 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class PhysicalNetwork < Network + has_many :nics + + protected + def validate + errors.add("name", "must be specified") unless name + errors.add("boot_type_id", "must be specified") unless boot_type_id + end +end diff --git a/src/app/models/usage.rb b/src/app/models/usage.rb new file mode 100644 index 0000000..353e8f4 --- /dev/null +++ b/src/app/models/usage.rb @@ -0,0 +1,21 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Usage < ActiveRecord::Base + has_and_belongs_to_many :networks, :join_table => 'networks_usages' +end diff --git a/src/app/models/vlan.rb b/src/app/models/vlan.rb new file mode 100644 index 0000000..2b96502 --- /dev/null +++ b/src/app/models/vlan.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Vlan < Network + has_many :bondings + + validates_presence_of :number + + protected + def validate + errors.add("name", "must be specified") unless name + errors.add("number", "must be specified") unless number + errors.add("boot_type_id", "must be specified") unless boot_type_id + end +end diff --git a/src/app/views/dashboard/index.html.erb b/src/app/views/dashboard/index.html.erb index 92cb4da..8815ebc 100644 --- a/src/app/views/dashboard/index.html.erb +++ b/src/app/views/dashboard/index.html.erb @@ -13,6 +13,13 @@ </table> </div> + <% if @can_modify %> + <h3>Networks</h3> + <a href="<%= url_for :controller => 'network', :action => 'list' %>"> + View / Edit + </a> + <% end %> + </div> <!-- end #tools --> </td> diff --git a/src/app/views/host/edit_network.rhtml b/src/app/views/host/edit_network.rhtml new file mode 100644 index 0000000..7ec3180 --- /dev/null +++ b/src/app/views/host/edit_network.rhtml @@ -0,0 +1,85 @@ +<%- content_for :title do -%> + Edit <%= @host.hostname %> Network Devices +<%- end -%> + +<%- content_for :description do -%> + Select and edit nics and bonded interfaces on <%= @host.hostname %> +<%- end -%> + +<div id="select-host-nic" class="popup-content-selection"> +<%= select_with_label "NICs", "nic", "id", + @host.nics. + collect{ |nic| [nic.mac, nic.id] }. + insert(0, "") %> +</div> + +<div id="select-host-bonding" class="popup-content-selection"> +<%= select_with_label "Bonded Interfaces", "bonding", "id", [] %> +</div> + +<div style="clear: both;"></div> + +<div id="selected_nic_bonding" class="selected_popup_content"></div> + +<div id="host_network_footer" class="popup-content-footer"> + <%= ok_footer %> +</div> + +<script type="text/javascript"> +function reset_nics_bonding_detail(){ + var data='Select NIC or Bonded Interface<br/>'; + + $("#selected_nic_bonding").html(data); + $("#host_network_footer").show(); +}; + +reset_nics_bonding_detail(); // run it once for inital content + +function reset_nics_select(){ + $("#nic_id option:first").attr("selected", true); +}; + +function reset_bonding_select(){ + // incase of new additions / deletions, repopulate select box + $.getJSON( + "<%= url_for :action => 'bondings_json', :id => @host.id %>", + {}, + function(j){ + var options = "<option value=''></option>" + + "<option value='New'>New</option>"; + for(var i = 0; i < j.length; i++){ + options += '<option value="' + j[i].id + '">' + j[i].name + + '</option>'; + } + $("#bonding_id").html(options); + }); + + $("#bonding_id option:first").attr("selected", true); +}; + +reset_bonding_select(); // run it once for initial content + +$("#nic_id").change(function () { + reset_bonding_select(); + if($('#nic_id').val() != ""){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', + :action => 'edit_nic'%>/" + $('#nic_id').val()); + $("#host_network_footer").hide(); + }else{ + reset_nics_bonding_detail(); + } +}); + +$("#bonding_id").change(function () { + reset_nics_select(); + if($('#bonding_id').val() == "New"){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'new_bonding', :host_id => @host.id %>"); + $("#host_network_footer").hide(); + }else if($('#bonding_id').val() != ""){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'edit_bonding'%>/" + $('#bonding_id').val()); + $("#host_network_footer").hide(); + }else{ + reset_nics_bonding_detail(); + } +}); +</script> diff --git a/src/app/views/host/show.rhtml b/src/app/views/host/show.rhtml index d1b05ad..bc39a62 100644 --- a/src/app/views/host/show.rhtml +++ b/src/app/views/host/show.rhtml @@ -17,6 +17,10 @@ <%= image_tag "icon_x.png" %> Clear VMs </a> <% end -%> + <%= link_to image_tag("icon_edit.png") +"Edit Network", + {:controller => 'host', + :action => 'edit_network', :id => @host.id}, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> <%- end -%> <%- end -%> <script type="text/javascript"> diff --git a/src/app/views/network/_bonding_form.rhtml b/src/app/views/network/_bonding_form.rhtml new file mode 100644 index 0000000..4ec0df1 --- /dev/null +++ b/src/app/views/network/_bonding_form.rhtml @@ -0,0 +1,46 @@ + <%= error_messages_for 'bonding' %> + <%= error_messages_for 'ip_address' %> + +<div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'bonding[host_id]', @host.id %> + + <div class="selected_popup_content_left">Name</div> + <div class="selected_popup_content_right"> + <%= text_field_with_label "", "bonding", "name" %> + </div> + + <div class="selected_popup_content_left">Interface Name</div> + <div class="selected_popup_content_right"> + <%= text_field_with_label "", "bonding", "interface_name" %> + </div> + + <div class="selected_popup_content_left">Bonding Type</div> + <div class="selected_popup_content_right"> + <%= select "bonding", "bonding_type_id", + @bonding_types.collect { |bt| [bt.label, bt.id ] } %> + </div> + + <% if @host.nics.size != 0 %> + <div class="selected_popup_content_left">NICs</div> + <div class="selected_popup_content_right"> + <select id="bonding_nic_ids" name="bonding[nic_ids][]" multiple="true"> + <%= options_from_collection_for_select @host.nics, "id", "mac", + @bonding ? @bonding.nics.collect{ |x| x.nic_id.to_i } : [] %> + </select> + </div> + <% end %> + + <%= render :partial => 'select', :locals => { :target => 'bonding' } %> + +<div id="static_ip_options" + style="<% unless ((@network && @network.boot_type_id == @static_boot_type.id) || + (!@network && @networks.size > 0 && + @networks[0].boot_type_id == @static_boot_type.id)) %> + display: none;<%end %>"> + <%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'bonding', + :parent_id => @bonding ? @bonding.id : nil } %> +</div> + + +</div> diff --git a/src/app/views/network/_form.rhtml b/src/app/views/network/_form.rhtml new file mode 100644 index 0000000..3f393f7 --- /dev/null +++ b/src/app/views/network/_form.rhtml @@ -0,0 +1,31 @@ +<%= error_messages_for 'network' %> + +<!--[form:network]--> +<%= hidden_field 'network', 'id' if @create %> + +<%= text_field_with_label "Name:", "network", "name", + {:style=>"width:250px;"} %> + +<%= select_with_label "Boot Type:", 'network', 'boot_type_id', + @boot_types.collect{ |bt| [bt.label, bt.id] }, + :style=>"width:250px;" %> + +<%= select_with_label "Type", "network", "type", + [[ "Physical Network", "PhysicalNetwork" ], + [ "VLAN", "Vlan" ] ] %> + +<div id="vlan_options" style="display: none;"> +<%= text_field_with_label "Number:", "network", "number", + {:style=>"width:250px;"} %> +</div> + + +<script type="text/javascript"> +$("#network_type").change(function () { + if($('#network_type').val() == "Vlan"){ + $("#vlan_options").show(); + }else{ + $("#vlan_options").hide(); + } +}).trigger('change'); +</script> diff --git a/src/app/views/network/_grid.rhtml b/src/app/views/network/_grid.rhtml new file mode 100644 index 0000000..6af0c05 --- /dev/null +++ b/src/app/views/network/_grid.rhtml @@ -0,0 +1,37 @@ +<% networks_per_page = 40 unless (defined? networks_per_page) and !(networks_per_page.nil?) %> +<% usepager = @networks.size > networks_per_page %> + +<div id="<%= table_id %>_div"> +<form id="<%= table_id %>_form"> +<table id="<%= table_id %>" style="display:none"></table> +</form> +</div> +<script type="text/javascript"> + $("#<%= table_id %>").flexigrid + ( + { + url: '<%= url_for :action => "networks_json" %>', + dataType: 'json', + colModel : [ + {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, + {display: 'Name', name: 'name', width : 180, align: 'left'}, + {display: 'Type', name: 'type', width : 180, align: 'left'}, + {display: 'Boot Type', name: 'boot_type', width : 180, align: 'left'} + ], + sortname: "name", + sortorder: "asc", + usepager: <%= usepager %>, + useRp: <%= usepager %>, + rp: <%= networks_per_page %>, + showTableToggleBtn: true, + onSelect: <%= on_select %> + } + ); + function <%= table_id %>checkbox(celDiv) + { + $(celDiv).html('<input type="checkbox" name="grid_checkbox'+$(celDiv).html()+'" class="grid_checkbox" value="'+$(celDiv).html()+'"/>'); + } + +</script> + + diff --git a/src/app/views/network/_ip_address_form.rhtml b/src/app/views/network/_ip_address_form.rhtml new file mode 100644 index 0000000..b952807 --- /dev/null +++ b/src/app/views/network/_ip_address_form.rhtml @@ -0,0 +1,62 @@ +<%= error_messages_for 'ip_address' %> + +<div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'parent_type', @parent_type%> + <%= hidden_field_tag 'ip_address[id]', @ip_address.id if @ip_address%> + <%= hidden_field_tag 'ip_address[network_id]', @network.id if @network %> + <%= hidden_field_tag 'ip_address[nic_id]', @nic.id if @nic %> + <%= hidden_field_tag 'ip_address[bonding_id]', @bonding.id if @bonding %> + + <div class="selected_nic_bonding_left">Type:</div> + <div class="selected_nic_bonding_right"> + <%= select_with_label "", "ip_address", "type", + [[ "ipv4", "IpV4Address" ], [ "ipv6", "IpV6Address" ] ] %> + </div> + + + <div class="static_ip_common_options"> + <div class="selected_nic_bonding_left">IP Address</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "address" %> + </div> + </div> + + <div id="static_ip_v4_options"> + <div class="selected_nic_bonding_left">Netmask</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "netmask" %> + </div> + + <div class="selected_nic_bonding_left">Broadcast</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "broadcast" %> + </div> + </div> + + <div id="static_ip_v6_options" style="display: none;"> + <div class="selected_nic_bonding_left">Prefix</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "prefix" %> + </div> + </div> + + <div class="static_ip_common_options"> + <div class="selected_nic_bonding_left">Gateway</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "gateway" %> + </div> + </div> + +</div> + +<script type="text/javascript"> + $("#ip_address_type").change(function () { + if($("#ip_address_type").val() == 'IpV4Address'){ + $("#static_ip_v4_options").show(); + $("#static_ip_v6_options").hide(); + }else{ + $("#static_ip_v4_options").hide(); + $("#static_ip_v6_options").show(); + } + }).trigger('change'); +</script> diff --git a/src/app/views/network/_ip_addresses_form.rhtml b/src/app/views/network/_ip_addresses_form.rhtml new file mode 100644 index 0000000..f833b2a --- /dev/null +++ b/src/app/views/network/_ip_addresses_form.rhtml @@ -0,0 +1,50 @@ +<div id="select_ip_address" class="popup-content-selection"> +<%= select_with_label "IP Addresses", "ip_address", "id", [] %> +</div> + +<div id="selected_ip_address" class="selected_popup_content"></div> + +<script type="text/javascript"> +function reset_selected_ip_address(){ + var data='Select IP Address<br/>'; + + $("#selected_ip_address").html(data); + $("#ip_addresses_footer").show(); +}; + +reset_selected_ip_address(); // run it once for inital content + +function reset_ip_address_select(){ + // incase of new additions / deletions, repopulate select box + $.getJSON( + "<%= url_for :action => 'ip_addresses_json', :id => parent_id, :parent_type => parent_type %>", + {}, + function(j){ + var options = "<option value=''></option>" + + "<option value='New'>New</option>"; + for(var i = 0; i < j.length; i++){ + options += '<option value="' + j[i].id + '">' + j[i].name + + '</option>'; + } + $("#ip_address_id").html(options); + }); + + $("#ip_address_id option:first").attr("selected", true); +}; + +reset_ip_address_select(); // run it once for initial content + +$("#ip_address_id").change(function () { + if($('#ip_address_id').val() == "New"){ + $("#selected_ip_address").load("<%= url_for :action => 'new_ip_address', :id => parent_id, :parent_type => parent_type %>"); + $("#ip_addresses_footer").hide(); + }else if($('#ip_address_id').val() != ""){ + $("#selected_ip_address").load("<%= url_for :action => 'edit_ip_address'%>/" + $('#ip_address_id').val() + "?parent_type=<%= parent_type %>"); + $("#ip_addresses_footer").hide(); + }else{ + reset_selected_ip_address(); + } +}); + +</script> + diff --git a/src/app/views/network/_select.rhtml b/src/app/views/network/_select.rhtml new file mode 100644 index 0000000..e69d9b0 --- /dev/null +++ b/src/app/views/network/_select.rhtml @@ -0,0 +1,32 @@ +<% target = 'nic' unless target + network_id = 'physical_network_id' if target == 'nic' + network_id = 'vlan_id' if target == 'bonding' + + %> + +<div class="selected_popup_content_left">Network:</div> +<div class="selected_popup_content_right"> + <%= select_with_label "", target, network_id, + @networks.collect { |n| [n.name + ' - ' + n.boot_type.label, n.id ] } %> + +</div> + +<script type="text/javascript"> + var static_network_ids=<%= static_network_ids = '[' + @networks.each { |n| + static_network_ids += ',' if static_network_ids != '[' + static_network_ids += n.id.to_s if n.boot_type_id == @static_boot_type.id + } + static_network_ids += ']' + static_network_ids %>; + + $("#<%=target %>_<%= network_id %>").change(function () { + $("#static_ip_options").hide(); + for(i=0; i<static_network_ids.length; ++i){ + if($("#<%= target %>_<%= network_id %>").val() == static_network_ids[i]){ + $("#static_ip_options").show(); + break; + } + }; + }); +</script> diff --git a/src/app/views/network/edit.rhtml b/src/app/views/network/edit.rhtml new file mode 100644 index 0000000..6360b3f --- /dev/null +++ b/src/app/views/network/edit.rhtml @@ -0,0 +1,34 @@ +<%- content_for :title do -%> + Edit Network +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + + <!-- DIALOG BODY --> + <form method="POST" action="<%= url_for :action => 'update' %>" id="network_form" > + <div class="dialog_form"> + <%= hidden_field_tag 'id', @network.id %> + <%= render :partial => 'form', :locals => { :create => false } %> + </div> + <!-- DIALOG FOOTER --> + <%= popup_footer("$('#network_form').submit()", "Edit Network") %> + </form> + +<script type="text/javascript"> +$(function() { + var networkoptions = { + target: '<%= url_for :action => 'update' %>', + dataType: 'json', + success: function(response, status) { + afterNetwork(response, status); + refresh_summary_static('networks_selection', + '<div class="selection_left"><div>Select a network.</div></div>'); + } + }; + + // bind form using 'ajaxForm' + $('#network_form').ajaxForm(networkoptions); +}); +</script> + + diff --git a/src/app/views/network/edit_bonding.rhtml b/src/app/views/network/edit_bonding.rhtml new file mode 100644 index 0000000..645e469 --- /dev/null +++ b/src/app/views/network/edit_bonding.rhtml @@ -0,0 +1,52 @@ +<form method="POST" + action="<%= url_for :action => 'update_bonding' %>" id="edit_bonding_form" > + <div id="selected_popup_content_header"> + Editing Bonded Interface + </div> + + <%= hidden_field_tag('id', @bonding.id) %> + <%= render :partial => 'bonding_form' %> + +</form> + +<form method="POST" action="<%= url_for :action => 'destroy_bonding' %>" id="delete_bonding_form" > + <%= hidden_field_tag('id', @bonding.id) %> +</form> + +<%= multi_button_popup_footer({"Edit Bonding" => + "$('#edit_bonding_form').submit()", + "Delete Bonding" => + "$('#delete_bonding_form').submit()"}) %> + +<script type="text/javascript"> +$(function() { + var edit_bonding_options = { + target: '<%= url_for :action => 'update_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + } + } + }; + + var delete_bonding_options = { + target: '<%= url_for :action => 'destroy_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind forms using 'ajaxForm' + $('#edit_bonding_form').ajaxForm(edit_bonding_options); + $('#delete_bonding_form').ajaxForm(delete_bonding_options); + +}); +</script> diff --git a/src/app/views/network/edit_ip_address.rhtml b/src/app/views/network/edit_ip_address.rhtml new file mode 100644 index 0000000..5a3cc18 --- /dev/null +++ b/src/app/views/network/edit_ip_address.rhtml @@ -0,0 +1,66 @@ +<% if @parent_type == 'network' %> +<form method="POST" + action="<%= url_for :action => 'update_ip_address' %>" + id="edit_ip_address_form" > +<% end %> + + <div id="selected_popup_content_header"> + Editing IP Address + </div> + + <% if @parent_type != 'network' %> + <a href="#" onClick="$('#delete_ip_address_form').submit()" style="color: blue;">Delete</a> + <% end %> + + <%= hidden_field_tag('id', @ip_address.id) %> + <%= render :partial => 'ip_address_form' %> + +<% if @parent_type == 'network' %> +</form> +<% end %> + +<form method="POST" + action="<%= url_for :action => 'destroy_ip_address' %>" + id="delete_ip_address_form" > + <%= hidden_field_tag('id', @ip_address.id) %> +</form> + +<% if @parent_type == 'network' %> +<%= multi_button_popup_footer({" Edit IP Address" => + "$('#edit_ip_address_form').submit()", + "Delete IP Address" => + "$('#delete_ip_address_form').submit()"}) %> +<% end %> + +<script type="text/javascript"> +$(function() { + var edit_ip_address_options = { + target: '<%= url_for :action => 'update_ip_address' %>', // target element to update + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + } + } + }; + + var delete_ip_address_options = { + target: '<%= url_for :action => 'delete_ip_address' %>', // target element to update + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + } + } + }; + + // bind forms using 'ajaxForm' + $('#edit_ip_address_form').ajaxForm(edit_ip_address_options); + $('#delete_ip_address_form').ajaxForm(delete_ip_address_options); + +}); +</script> diff --git a/src/app/views/network/edit_network_ip_addresses.rhtml b/src/app/views/network/edit_network_ip_addresses.rhtml new file mode 100644 index 0000000..7a1e4cb --- /dev/null +++ b/src/app/views/network/edit_network_ip_addresses.rhtml @@ -0,0 +1,13 @@ +<%- content_for :title do -%> + Edit Network IP Addresses +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + +<%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'network', + :parent_id => @network.id } %> + +<div id="ip_addresses_footer" class="popup-content-footer"> + <%= ok_footer %> +</div> diff --git a/src/app/views/network/edit_nic.rhtml b/src/app/views/network/edit_nic.rhtml new file mode 100644 index 0000000..75af6fb --- /dev/null +++ b/src/app/views/network/edit_nic.rhtml @@ -0,0 +1,62 @@ +<form method="POST" + action="<%= url_for :action => 'update_nic' %>" id="nic_form" > + <div id="selected_popup_content_header"> + Editing NIC + </div> + + <%= error_messages_for 'nic' %> + + <div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'id', @nic.id %> + <%= hidden_field_tag 'nic_host_id', @nic.host.id %> + <%= hidden_field_tag 'nic_network_id', @nic.physical_network.id %> + + <div class="selected_popup_content_left">MAC:</div> + <div class="selected_popup_content_right"><%= @nic.mac %></div> + + <% if @nic.host.bondings.size != 0 %> + <div class="selected_popup_content_left">Bonded Interfaces</div> + <div class="selected_popup_content_right"> + <select id="nic_bonding_ids" name="nic[bonding_ids][]"> + <option value="" /> + <%= options_from_collection_for_select @nic.host.bondings, + "id", "name", + @nic.bondings.size > 0 ? @nic.bondings[0].bonding_id.to_i : "" %> + </select> + </div> + <% end %> + + + <%= render :partial => 'select' %> + + <div id="static_ip_options" + style="<% if @network.boot_type_id != @static_boot_type.id %> + display: none;<%end %>"> + <%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'nic', + :parent_id => @nic.id } %> + </div> + + + </div> + <%= popup_footer("$('#nic_form').submit()", "Edit NIC") %> +</form> + +<script type="text/javascript"> +$(function() { + var nicoptions = { + target: '<%= url_for :action => 'update_nic' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_nics_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind form using 'ajaxForm' + $('#nic_form').ajaxForm(nicoptions); +}); +</script> diff --git a/src/app/views/network/list.html.erb b/src/app/views/network/list.html.erb new file mode 100644 index 0000000..9a304cf --- /dev/null +++ b/src/app/views/network/list.html.erb @@ -0,0 +1,72 @@ +<div id="toolbar_nav"> +<ul> + <li><a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]"><%= image_tag "icon_addstorage.png", :style => "vertical-align:middle;" %> Add Network</a></li> + <li> + <a href="#" onClick="delete_networks();" > + <%= image_tag "icon_remove.png", :style=>"vertical-align:middle;" %> + Remove + </a> + </li> + </ul> +</div> + +<script type="text/javascript"> + function delete_networks(){ + var networks = get_selected_networks(); + if (validate_selected(get_selected_networks(), 'networks')) { + $.post('<%= url_for :action => 'delete' %>', + { network_ids: networks.toString() }, + function(data,status){ + if (data.alert) { + $.jGrowl(data.alert); + } + grid = $("#networks_grid"); + if (grid.size()>0 && grid != null) { + grid.flexReload(); + } + empty_summary('networks_selection', 'Network'); + }, 'json'); + } + } + + function networks_select(selected_rows) + { + var selected_ids = new Array(); + for(i=0; i<selected_rows.length; i++) { + selected_ids[i] = selected_rows[i].id; + } + if (selected_ids.length == 1) + { + $('#networks_selection').load('<%= url_for :action => "show" %>', + { id: parseInt(selected_ids[0].substring(3))}); + } + } + +</script> + +<div class="panel_header"></div> +<% if @networks.size != 0 %> + <div class="data_section"> + <%= render :partial => "grid", :locals => { :table_id => "networks_grid", + :networks => @networks, + :on_select => "networks_select"} %> + </div> + + <div class="selection_detail" id="networks_selection"> + <div class="selection_left"> + <div>Select a network.</div> + </div> + </div> +<% else %> + <div class="data_section"> + <div class="no-grid-items"> + <%= image_tag 'no-grid-items.png', :style => 'float: left;' %> + + <div class="no-grid-items-text"> + No networks found. <br/><br/> + <%= image_tag "icon_addhost.png", :style=>"vertical-align:middle;" %> + <a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]">Add first network</a> + </div> + </div> + </div> +<% end %> diff --git a/src/app/views/network/new.rhtml b/src/app/views/network/new.rhtml new file mode 100644 index 0000000..15b7304 --- /dev/null +++ b/src/app/views/network/new.rhtml @@ -0,0 +1,28 @@ +<%- content_for :title do -%> + Add Network +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + + <!-- DIALOG BODY --> + <form method="POST" action="<%= url_for :action => 'create' %>" id="network_form" > + <div class="dialog_form"> + <%= render :partial => 'form', :locals => { :create => true } %> + </div> + <!-- DIALOG FOOTER --> + <%= popup_footer("$('#network_form').submit()", "Add Network") %> + </form> + +<script type="text/javascript"> +$(function() { + var networkoptions = { + target: '<%= url_for :action => 'create' %>', + dataType: 'json', + success: afterNetwork // post-submit callback + }; + + // bind form using 'ajaxForm' + $('#network_form').ajaxForm(networkoptions); +}); +</script> + diff --git a/src/app/views/network/new_bonding.rhtml b/src/app/views/network/new_bonding.rhtml new file mode 100644 index 0000000..9ee6994 --- /dev/null +++ b/src/app/views/network/new_bonding.rhtml @@ -0,0 +1,32 @@ +<form method="POST" + action="<%= url_for :action => 'create_bonding' %>" id="bonding_form" > + + <div id="selected_popup_content_header"> + Create Bonded Interface + </div> + + <%= render :partial => 'bonding_form' %> +</form> + +<%= multi_button_popup_footer({"Create Bonding" => + "$('#bonding_form').submit()"}) %> + +<script type="text/javascript"> +$(function() { + var bonding_options = { + target: '<%= url_for :action => 'create_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind forms using 'ajaxForm' + $('#bonding_form').ajaxForm(bonding_options); + +}); +</script> diff --git a/src/app/views/network/new_ip_address.rhtml b/src/app/views/network/new_ip_address.rhtml new file mode 100644 index 0000000..6167a76 --- /dev/null +++ b/src/app/views/network/new_ip_address.rhtml @@ -0,0 +1,33 @@ +<% if @parent_type == 'network' %> +<form method="POST" + action="<%= url_for :action => 'create_ip_address' %>" + id="ip_address_form" > +<% end %> + + <div id="selected_popup_content_header"> + Create Ip Address + </div> + + <%= render :partial => 'ip_address_form' %> + +<% if @parent_type == 'network' %> +</form> +<% end %> + +<% if @parent_type == 'network' %> +<%= multi_button_popup_footer({"Create IP Address" => + "$('#ip_address_form').submit()"}) %> +<% end %> + +<script type="text/javascript"> +$(function() { + var ip_address_options = { + target: '<%= url_for :action => 'create_ip_address' %>', + dataType: 'json', + success: afterIpAddress + }; + + // bind forms using 'ajaxForm' + $('#ip_address_form').ajaxForm(ip_address_options); +}); +</script> diff --git a/src/app/views/network/show.rhtml b/src/app/views/network/show.rhtml new file mode 100644 index 0000000..a56c41d --- /dev/null +++ b/src/app/views/network/show.rhtml @@ -0,0 +1,30 @@ +<%- content_for :title do -%> + <%=h @network.name %> +<%- end -%> + +<%- content_for :action_links do -%> + <%= link_to image_tag("icon_edit.png") + "Edit", + {:action => 'edit', :id => @network.id }, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> + + <%= link_to image_tag("icon_edit.png") + "Edit IP Addresses", + {:action => 'edit_network_ip_addresses', :id => @network.id }, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> +<%- end -%> + +<div id="hosts_selection_id" style="display:none"><%= @host.id %></div> +<div class="selection_key"> + Name:<br/> + Type:<br/> + Boot Type:<br/> + IP Addresses:<br/> +</div> +<div class="selection_value"> + <%=h @network.name %><br/> + <%=h @network.type %><br/> + <%=h @network.boot_type.label %><br/> + <%=h @network.ip_addresses.size %><br/> +</div> + +<%- content_for :right do -%> +<%- end -%> diff --git a/src/app/views/nic/_list.rhtml b/src/app/views/nic/_list.rhtml index 07f7e44..eb3a4a7 100644 --- a/src/app/views/nic/_list.rhtml +++ b/src/app/views/nic/_list.rhtml @@ -1,7 +1,6 @@ <table class='listing'> <thead> <tr> - <th class="empty">IP</th> <th>MAC</th> <th>Bridge</th> <th>Usage Type</th> @@ -11,7 +10,6 @@ </thead> <% for nic in nics %> <tr class="<%= cycle('odd','even', :name => nics) %>"> - <td><%= link_to nic.ip_addr, { :controller => "nic", :action => 'show', :id => nic }, { :class => "show" } %></td> <td><%= nic.mac %></td> <td><%= nic.bridge %></td> <td><%= nic.usage_type %></td> diff --git a/src/db/migrate/028_refactor_networking_model.rb b/src/db/migrate/028_refactor_networking_model.rb new file mode 100644 index 0000000..946b976 --- /dev/null +++ b/src/db/migrate/028_refactor_networking_model.rb @@ -0,0 +1,271 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# introduce networks and ip_addresses tables, refactor relationships +class RefactorNetworkingModel < ActiveRecord::Migration + def self.up + + #################################################### + # bugfix, bridge tables shouldn't have their own ids + remove_column :bondings_nics, :id + + ################################################################## + # add networks, usages tables and networks_usage_types bridge + create_table :networks do |t| + t.string :type, :null => false + t.string :name, :null => false + t.integer :boot_type_id, :null => false + + # attributes for Vlan (type=Vlan) + t.integer :number + end + + create_table :usages do |t| + t.string :label, :null => false + t.string :usage, :null => false + end + + create_table :networks_usages, :id => false do |t| + t.integer :network_id, :null => false + t.integer :usage_id, :null => false + end + + add_index :networks_usages, [:network_id, :usage_id], :unique => true + + # create usages + Usage.create(:label => 'Guest', :usage => 'guest') + Usage.create(:label => 'Management', :usage => 'management') + Usage.create(:label => 'Storage', :usage => 'storage') + + # referential integrity for networks tables + execute "alter table networks add constraint + fk_network_boot_types + foreign key (boot_type_id) references + boot_types(id)" + execute "alter table networks_usages add constraint + fk_networks_usages_network_id + foreign key (network_id) references + networks(id)" + execute "alter table networks_usages add constraint + fk_networks_usages_usage_id + foreign key (usage_id) references + usages(id)" + + # add foreign keys to nics / bondings table + add_column :nics, :physical_network_id, :integer + add_column :bondings, :vlan_id, :integer + + # referential integrity for nic/bondings network ids + execute "alter table nics add constraint + fk_nic_networks + foreign key (physical_network_id) references + networks(id)" + execute "alter table bondings add constraint + fk_bonding_networks + foreign key (vlan_id) references + networks(id)" + + #################################################### + # create ip_addresses table + create_table :ip_addresses do |t| + t.string :type + + # foreign keys to associated entities + t.integer :nic_id + t.integer :bonding_id + t.integer :network_id + + # common attributes + t.string :address, :limit => 39, :null => false + t.string :gateway, :limit => 39 + + # attributes for IPv4 (type=IpV4Address) + t.string :netmask, :limit => 15 + t.string :broadcast, :limit => 15 + + # attributes for IPv6 (type=IpV6Address) + t.string :prefix, :limit => 39 + t.timestamps + end + + # referential integrity for ip_addresses table + execute "alter table ip_addresses add constraint + fk_nic_ip_addresses + foreign key (nic_id) references nics(id)" + execute "alter table ip_addresses add constraint + fk_bonding_ip_addresses + foreign key (bonding_id) references bondings(id)" + execute "alter table ip_addresses add constraint + fk_network_ip_addresses + foreign key (network_id) references networks(id)" + + ################################################################### + static_boot_type_id + BootType.find(:first, + :conditions => {:proto => 'static'} ).id + + # migrate nic ip_addresses to networks / ip_addresses table + i = 0 + Nic.find(:all).each do |nic| + if nic.boot_type_id == static_boot_type_id + IpV4Address.new(:nic_id => nic.id, + :address => nic.ip_addr).save! + + end + network = PhysicalNetwork.new( + :name => 'Physical Network ' + i.to_s, + :boot_type_id => nic.boot_type_id) + network.save! + + ip_address = IpV4Address.new(:address => nic.ip_addr ? nic.ip_addr : '0.0.0.0', + :netmask => nic.netmask, + :broadcast => nic.broadcast, + :gateway => nic.ip_addr) + ip_address.network = network + ip_address.save! + + nic.physical_network = network + nic.save! + + i += 1 + end + + # migrate bonding ip_addresses to networks / ip_addresses table + i = 0 + Bonding.find(:all).each do |bonding| + if bonding.boot_type_id == static_boot_type_id + IpV4Address.new(:bonding_id => bonding.id, + :address => bonding.ip_addr).save! + end + network = Vlan.new( + :name => 'VLAN ' + i.to_s, + :number => i, + :boot_type_id => bonding.boot_type_id) + network.save! + + ip_address = IpV4Address.new(:address => bonding.ip_addr ? bonding.ip_addr : '0.0.0.0', + :netmask => bonding.netmask, + :broadcast => bonding.broadcast, + :gateway => bonding.ip_addr) + ip_address.network = network + ip_address.save! + + bonding.vlan = network + bonding.save! + + i += 1 + end + + ############################################################## + # remove nics / bonding ip address and network related columns + remove_column :nics, :ip_addr + remove_column :nics, :netmask + remove_column :nics, :broadcast + remove_column :nics, :boot_type_id + remove_column :bondings, :ip_addr + remove_column :bondings, :netmask + remove_column :bondings, :broadcast + remove_column :bondings, :boot_type_id + + + end + + def self.down + ############################################################## + # readd nics / bonding ip address related columns + add_column :nics, :ip_addr, :string, :limit => 16 + add_column :nics, :netmask, :string, :limit => 16 + add_column :nics, :broadcast, :string, :limit => 16 + add_column :nics, :boot_type_id, :integer + add_column :bondings, :ip_addr, :string, :limit => 16 + add_column :bondings, :netmask, :string, :limit => 16 + add_column :bondings, :broadcast, :string, :limit => 16 + add_column :bondings, :boot_type_id, :integer + + execute "alter table nics add constraint + fk_nic_boot_types + foreign key (boot_type_id) references + boot_types(id)" + execute "alter table bondings add constraint + fk_bonding_boot_types + foreign key (boot_type_id) references + boot_types(id)" + + ############################################################## + # attempt to migrate ip information back into nics table. + # because a nic can have multiple ips (if statically + # assigned) as well as its network, just use the 1st + # found + Nic.find(:all).each do |nic| + if nic.physical_network.ip_addresses.size > 0 + # use the 1st configured network ip + nic.ip_addr = nic.physical_network.ip_addresses[0].address + nic.netmask = nic.physical_network.ip_addresses[0].netmask + nic.broadcast = nic.physical_network.ip_addresses[0].broadcast + end + + if nic.ip_addresses.size > 0 + # use the 1st assigned static ip + nic.ip_addr = nic.ip_addresses[0].address + end + + nic.boot_type_id = nic.physical_network.boot_type_id + + nic.save! + end + + # attempt to migrate ip information back into bondings table. + # because a bonding can have multiple ips (if statically + # assigned) as well as its network, just use the 1st + # found + Bonding.find(:all).each do |bonding| + if bonding.vlan.ip_addresses.size > 0 + # use the 1st configured network ip + bonding.ip_addr = bonding.vlan.ip_addresses[0].address + bonding.netmask = bonding.vlan.ip_addresses[0].netmask + bonding.broadcast = bonding.vlan.ip_addresses[0].broadcast + end + + if bonding.ip_addresses.size > 0 + # use the 1st assigned static ip + bonding.ip_addr = bonding.ip_addresses[0].address + end + + bonding.boot_type_id = bonding.vlan.boot_type_id + + bonding.save! + end + + ############################################################## + # drop ip_addresses table + drop_table :ip_addresses + + # drop network ids from nics / bondings table + remove_column :nics, :physical_network_id + remove_column :bondings, :vlan_id + + # drop networks tables + drop_table :networks_usages + drop_table :usages + drop_table :networks + + ############################################################## + # undo bugfix above + add_column :bondings_nics, :id, :integer + end +end diff --git a/src/public/javascripts/ovirt.js b/src/public/javascripts/ovirt.js index 4579c80..818b862 100644 --- a/src/public/javascripts/ovirt.js +++ b/src/public/javascripts/ovirt.js @@ -297,4 +297,31 @@ function delete_pool(delete_url, id) $.jGrowl(data.alert); } }, 'json'); -} \ No newline at end of file +} + + +function get_selected_networks() +{ + return get_selected_checkboxes("networks_grid_form"); +} + +function afterNetwork(response, status){ + ajax_validation(response, status); + if (response.success) { + $(document).trigger('close.facebox'); + grid = $("#networks_grid"); + if (grid.size()>0 && grid != null) { + grid.flexReload(); + } else { + $tabs.tabs("load",$tabs.data('selected.tabs')); + } + } +} + +function afterIpAddress(response, status){ + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + } +} diff --git a/src/public/stylesheets/components.css b/src/public/stylesheets/components.css index 16eaf62..228ff7b 100644 --- a/src/public/stylesheets/components.css +++ b/src/public/stylesheets/components.css @@ -267,4 +267,52 @@ .detail-pane-chart { height: 50px; width: 375px; -}; +} + + +/************************* + * new popup components + *************************/ +.popup-content-selection { + float: left; + width: 45%; + padding-left: 20px; + padding-top: 10px; +} + +.popup-content-selection select{ + min-width: 200px; +} + +.popup-content-footer{ + width: 99%; + float: left; +} + +.selected_popup_content { + padding-left: 20px; + min-height: 50px; + float: left; + width: 96%; +} + +#selected_popup_content_header { + padding-bottom: 5px; + font-weight: bold; +} + +#selected_popup_content_expanded{ + padding-bottom: 50px; +} + +.selected_popup_content_left { + float: left; + width: 40%; + padding-bottom: 15px; +} + +.selected_popup_content_right { + float: left; + width: 40%; + padding-bottom: 15px; +} -- 1.5.6.5
Darryl Pierce
2008-Oct-31 14:06 UTC
[Ovirt-devel] [PATCH server] network integration into ovirt server db and wui
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 NAK. Some of the first things I've noticed is that, by removing columns from the bondings and nics tables into the separate IP-related tables, updates need to be made to the ManagedNodeConfiguration class. Since the data itself is the same and just relocated, refactoring the configuration class to get data from the appropriate objects should take care of things initially. Before this patch can be pushed, it needs to fix that class. The unit test itself does not need changing since the data ought to still look the same once encoded. There may be changes needed for vlans, but that will be additional tests. Comments are inline. Mohammed Morsi wrote:> --- > src/app/controllers/host_controller.rb | 12 +- > src/app/controllers/network_controller.rb | 427 ++++++++++++++++++++ > src/app/helpers/application_helper.rb | 28 +- > src/app/helpers/network_helper.rb | 2 + > src/app/models/bonding.rb | 27 +- > src/app/models/ip_address.rb | 27 ++ > src/app/models/ip_v4_address.rb | 48 +++ > src/app/models/ip_v6_address.rb | 44 ++ > src/app/models/network.rb | 37 ++ > src/app/models/nic.rb | 16 +- > src/app/models/physical_network.rb | 27 ++ > src/app/models/usage.rb | 21 + > src/app/models/vlan.rb | 30 ++ > src/app/views/dashboard/index.html.erb | 7 + > src/app/views/host/edit_network.rhtml | 85 ++++ > src/app/views/host/show.rhtml | 4 + > src/app/views/network/_bonding_form.rhtml | 46 +++ > src/app/views/network/_form.rhtml | 31 ++ > src/app/views/network/_grid.rhtml | 37 ++ > src/app/views/network/_ip_address_form.rhtml | 62 +++ > src/app/views/network/_ip_addresses_form.rhtml | 50 +++ > src/app/views/network/_select.rhtml | 32 ++ > src/app/views/network/edit.rhtml | 34 ++ > src/app/views/network/edit_bonding.rhtml | 52 +++ > src/app/views/network/edit_ip_address.rhtml | 66 +++ > .../views/network/edit_network_ip_addresses.rhtml | 13 + > src/app/views/network/edit_nic.rhtml | 62 +++ > src/app/views/network/list.html.erb | 72 ++++ > src/app/views/network/new.rhtml | 28 ++ > src/app/views/network/new_bonding.rhtml | 32 ++ > src/app/views/network/new_ip_address.rhtml | 33 ++ > src/app/views/network/show.rhtml | 30 ++ > src/app/views/nic/_list.rhtml | 2 - > src/db/migrate/028_refactor_networking_model.rb | 271 +++++++++++++ > src/public/javascripts/ovirt.js | 29 ++- > src/public/stylesheets/components.css | 50 +++- > 36 files changed, 1849 insertions(+), 25 deletions(-) > create mode 100644 src/app/controllers/network_controller.rb > create mode 100644 src/app/helpers/network_helper.rb > create mode 100644 src/app/models/ip_address.rb > create mode 100644 src/app/models/ip_v4_address.rb > create mode 100644 src/app/models/ip_v6_address.rb > create mode 100644 src/app/models/network.rb > create mode 100644 src/app/models/physical_network.rb > create mode 100644 src/app/models/usage.rb > create mode 100644 src/app/models/vlan.rb > create mode 100644 src/app/views/host/edit_network.rhtml > create mode 100644 src/app/views/network/_bonding_form.rhtml > create mode 100644 src/app/views/network/_form.rhtml > create mode 100644 src/app/views/network/_grid.rhtml > create mode 100644 src/app/views/network/_ip_address_form.rhtml > create mode 100644 src/app/views/network/_ip_addresses_form.rhtml > create mode 100644 src/app/views/network/_select.rhtml > create mode 100644 src/app/views/network/edit.rhtml > create mode 100644 src/app/views/network/edit_bonding.rhtml > create mode 100644 src/app/views/network/edit_ip_address.rhtml > create mode 100644 src/app/views/network/edit_network_ip_addresses.rhtml > create mode 100644 src/app/views/network/edit_nic.rhtml > create mode 100644 src/app/views/network/list.html.erb > create mode 100644 src/app/views/network/new.rhtml > create mode 100644 src/app/views/network/new_bonding.rhtml > create mode 100644 src/app/views/network/new_ip_address.rhtml > create mode 100644 src/app/views/network/show.rhtml > create mode 100644 src/db/migrate/028_refactor_networking_model.rb > > diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb > index a40d297..120fe2a 100644 > --- a/src/app/controllers/host_controller.rb > +++ b/src/app/controllers/host_controller.rb > @@ -30,7 +30,7 @@ class HostController < ApplicationController > end > end > > - before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms] > + before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms, :edit_network] > > # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) > verify :method => [:post, :put], :only => [ :create, :update ], > @@ -156,6 +156,16 @@ class HostController < ApplicationController > render :json => @json_hash > end > > + def edit_network > + render :layout => 'popup' > + end > + > + def bondings_json > + bondings = Host.find(params[:id]).bondings > + bondings_json = [] > + bondings.each{ |x| bondings_json.push({:id => x.id, :name => x.name}) } > + render :json => bondings_json > + end > > private > #filter methods > diff --git a/src/app/controllers/network_controller.rb b/src/app/controllers/network_controller.rb > new file mode 100644 > index 0000000..b7f8b6d > --- /dev/null > +++ b/src/app/controllers/network_controller.rb > @@ -0,0 +1,427 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > +# > + > +class NetworkController < ApplicationController > + ########################## Networks related actions > + > + def network_permissions > + # TODO more robust permission system > + # either by subclassing network from pool > + # or by extending permission model to accomodate > + # any object > + @default_pool = HardwarePool.get_default_pool > + set_perms(@default_pool) > + unless @can_modify > + flash[:notice] = 'You do not have permission to view networks' > + redirect_to :controller => 'dashboard' > + end > + end > + > + def list > + @networks = Network.find(:all) > + network_permissions > + end > + > + def networks_json > + json_list(Network.find(:all), [:id, :name, :type, [:boot_type, :label]]) > + end > + > + def show > + @network = Network.find(params[:id]) > + network_permissions > + respond_to do |format| > + format.html { render :layout => 'selection' } > + format.xml { render :xml => @network.to_xml } > + end > + end > + > + def new > + @boot_types = BootType.find(:all) > + render :layout => 'popup' > + end > + > + def create > + begin > + @network = PhysicalNetwork.new(params[:network]) if params[:network][:type] == 'PhysicalNetwork' > + @network = Vlan.new(params[:network]) if params[:network][:type] == 'Vlan' > + @network.save! > + alert = "Network was successfully created." > + render :json => { :object => "network", :success => true, > + :alert => alert } > + rescue > + render :json => { :object => "network", :success => false, > + :errors => > + @network.errors.localize_error_messages.to_a } > + end > + end > + > + def edit > + @network = Network.find(params[:id]) > + @boot_types = BootType.find(:all) > + render :layout => 'popup' > + end > + > + def update > + begin > + @network = Network.find(params[:id]) > + > + # special case if we are switching types > + if @network.type != params[:network][:type] > + if ! @network.conversion_valid? > + render :json => { :object => "network", :success => false, > + :alert => > + 'Can not change type of network with associated nics/bondings'} > + return > + end > + > + if params[:network][:type] == 'PhysicalNetwork' > + Vlan.destroy(params[:id]) > + @network = PhysicalNetwork.create(params[:network]) > + else > + PhysicalNetwork.destroy(params[:id]) > + @network = Vlan.create(params[:network]) > + end > + else > + @network = PhysicalNetwork.find(params[:id]) if @network.type == 'PhysicalNetwork' > + @network = Vlan.find(params[:id]) if @network.type == 'Vlan' > + @network.update_attributes!(params[:network]) > + end > + > + alert = "Network was successfully updated." > + render :json => { :object => "network", :success => true, > + :alert => alert } > + rescue Exception => e > + render :json => { :object => "network", :success => false, > + :errors => > + @network.errors.localize_error_messages.to_a } > + end > + end > + > + def delete > + failed_networks = [] > + networks_ids_str = params[:network_ids] > + network_ids = networks_ids_str.split(",").collect {|x| x.to_i} > + network_ids.each{ |x| > + network = Network.find(x) > + if network.conversion_valid? > + begin > + Network.destroy(x) > + rescue > + failed_networks.push x > + end > + else > + failed_networks.push x > + end > + } > + if failed_networks.size == 0 > + render :json => { :object => "network", > + :success => true, > + :alert => "Successfully deleted networks" } > + else > + render :json => { :object => "network", > + :success => false, > + :alert => "Failed deleting " + > + failed_networks.size.to_s + > + " networks due to existing bondings/nics" } > + end > + end > + > + def edit_network_ip_addresses > + @network = Network.find(params[:id]) > + render :layout => 'popup' > + end > + > + > + ########################## Ip Address related actions > + > + def ip_addresses_json > + @parent_type = params[:parent_type] > + if @parent_type == 'network' > + ip_addresses = Network.find(params[:id]).ip_addresses > + elsif @parent_type == 'nic' > + ip_addresses = Nic.find(params[:id]).ip_addresses > + elsif @parent_type == 'bonding' and params[:id] > + ip_addresses = Bonding.find(params[:id]).ip_addresses > + else > + ip_addresses = [] > + end > + > + ip_addresses_json = [] > + ip_addresses.each{ |x| > + ip_addresses_json.push({:id => x.id, :name => x.address}) } > + render :json => ip_addresses_json > + end > + > + def new_ip_address > + @parent_type = params[:parent_type] > + @network = Network.find(params[:id]) if @parent_type == 'network' > + @nic = Nic.find(params[:id]) if @parent_type == 'nic' > + @bonding = Bonding.find(params[:id]) if @parent_type == 'bonding' and params[:id] > + > + render :layout => false > + end > + > + def _create_ip_address > + if params[:ip_address][:type] == "IpV4Address" > + @ip_address = IpV4Address.new(params[:ip_address]) > + else > + @ip_address = IpV6Address.new(params[:ip_address]) > + end > + @ip_address.save! > + end > + > + def create_ip_address > + begin > + _create_ip_address > + alert = "Ip Address was successfully created." > + render :json => { :object => "ip_address", :success => true, > + :alert => alert } > + rescue > + render :json => { :object => "ip_address", :success => false, > + :errors => > + @ip_address.errors.localize_error_messages.to_a } > + end > + end > + > + def edit_ip_address > + @ip_address = IpAddress.find(params[:id]) > + > + @parent_type = params[:parent_type] > + @network = @ip_address.network if @ip_address.network_id > + @nic = @ip_address.nic if @ip_address.nic_id > + @bonding = @ip_address.bonding if @ip_address.bonding_id > + > + render :layout => false > + end > + > + def _update_ip_address(id) > + @ip_address = IpAddress.find(id) > + > + # special case if we are switching types > + if @ip_address.type != params[:ip_address][:type] > + if params[:ip_address][:type] == 'IpV4Address' > + @ip_address = IpV4Address.new(params[:ip_address]) > + @ip_address.save! > + IpV6Address.delete(id) > + else > + @ip_address = IpV6Address.new(params[:ip_address]) > + @ip_address.save! > + IpV4Address.delete(id) > + end > + else > + if @ip_address.type == 'IpV4Address' > + @ip_address = IpV4Address.find(id) > + else > + @ip_address = IpV6Address.find(id) > + end > + @ip_address.update_attributes!(params[:ip_address]) > + end > + > + end > + > + def update_ip_address > + begin > + _update_ip_address(params[:id]) > + alert = "IpAddress was successfully updated." > + render :json => { :object => "network", :success => true, > + :alert => alert } > + rescue > + render :json => { :object => "ip_address", :success => false, > + :errors => > + @ip_address.errors.localize_error_messages.to_a } > + end > + end > + > + def destroy_ip_address > + begin > + IpAddress.delete(params[:id]) > + alert = "Ip Address was successfully deleted." > + render :json => { :object => "ip_address", :success => true, > + :alert => alert } > + rescue > + render :json => { :object => "ip_address", :success => false, > + :alert => 'Ip Address Deletion Failed' } > + end > + end > + > + > + ########################## NICs related actions > + > + def edit_nic > + @nic = Nic.find(params[:id]) > + @network = @nic.physical_network > + > + @networks = PhysicalNetwork.find(:all) > + network_options > + > + render :layout => false > + end > + > + def update_nic > + begin > + network_options > + @network = Network.find(params[:nic][:physical_network_id]) > + > + if @network.boot_type.id == @static_boot_type.id > + if params[:ip_address][:id] == "New" > + _create_ip_address > + elsif params[:ip_address][:id] != "" > + _update_ip_address(params[:ip_address][:id]) > + end > + end > + > + @nic = Nic.find(params[:id]) > + @nic.update_attributes!(params[:nic]) > + > + alert = "Nic was successfully updated." > + render :json => { :object => "nic", :success => true, > + :alert => alert } > + rescue Exception => e > + if @ip_address and @ip_address.errors.size != 0 > + render :json => { :object => "ip_address", :success => false, > + :errors => > + @ip_address.errors.localize_error_messages.to_a} > + else > + render :json => { :object => "nic", :success => false, > + :errors => > + @nic.errors.localize_error_messages.to_a } > + end > + end > + end > + > + ########################## Bonding related actions > + > + def new_bonding > + unless params[:host_id] > + flash[:notice] = "Host is required." > + redirect_to :controller => 'dashboard' > + end > + > + @host = Host.find(params[:host_id]) > + @networks = Vlan.find(:all) > + network_options > + > + render :layout => false > + end > + > + def create_bonding > + begin > + network_options > + @network = Network.find(params[:bonding][:vlan_id]) > + > + if @network.boot_type.id == @static_boot_type.id > + if params[:ip_address][:id] == "New" > + _create_ip_address > + elsif params[:ip_address][:id] != "" > + _update_ip_address(params[:ip_address][:id]) > + end > + end > + > + @bonding = Bonding.new(params[:bonding]) > + @bonding.save! > + > + if @ip_address > + @ip_address.bonding_id = @bonding.id > + @ip_address.save! > + end > + > + alert = "Bonding was successfully created." > + render :json => { :object => "bonding", :success => true, > + :alert => alert } > + rescue > + if @ip_address and @ip_address.errors.size != 0 > + render :json => { :object => "ip_address", :success => false, > + :errors => > + @ip_address.errors.localize_error_messages.to_a} > + else > + render :json => { :object => "bonding", :success => false, > + :errors => > + @bonding.errors.localize_error_messages.to_a } > + end > + end > + end > + > + def edit_bonding > + @bonding = Bonding.find(params[:id]) > + @network = @bonding.vlan > + > + @host = @bonding.host > + @networks = Vlan.find(:all) > + network_options > + > + render :layout => false > + end > + > + def update_bonding > + begin > + network_options > + @network = Network.find(params[:bonding][:vlan_id]) > + > + if @network.boot_type.id == @static_boot_type.id > + if params[:ip_address][:id] == "New" > + _create_ip_address > + elsif params[:ip_address][:id] != "" > + _update_ip_address(params[:ip_address][:id]) > + end > + end > + > + @bonding = Bonding.find(params[:id]) > + @bonding.nics.each { |nic| @bonding.nics.delete(nic) } > + @bonding.update_attributes!(params[:bonding]) > + > + alert = "Bonding was successfully updated." > + render :json => { :object => "bonding", :success => true, > + :alert => alert } > + rescue > + if @ip_address and @ip_address.errors.size != 0 > + render :json => { :object => "ip_address", :success => false, > + :errors => > + @ip_address.errors.localize_error_messages.to_a} > + else > + render :json => { :object => "bonding", :success => false, > + :errors => > + @bonding.errors.localize_error_messages.to_a } > + end > + end > + end > + > + def destroy_bonding > + begin > + Bonding.destroy(params[:id]) > + alert = "Bonding was successfully deleted." > + render :json => { :object => "bonding", :success => true, > + :alert => alert } > + rescue > + render :json => { :object => "bonding", :success => false, > + :alert => 'Bonding Deletion Failed' } > + end > + > + end > + > + > + ########################## Misc methods > + > + protected > + def network_options > + @bonding_types = BondingType.find(:all) > + @static_boot_type = BootType.find(:first, > + :conditions => { :proto => 'static' }) > + end > + > +end > diff --git a/src/app/helpers/application_helper.rb b/src/app/helpers/application_helper.rb > index d7b6628..0178ad0 100644 > --- a/src/app/helpers/application_helper.rb > +++ b/src/app/helpers/application_helper.rb > @@ -84,21 +84,31 @@ module ApplicationHelper > } > end > > - def popup_footer(action, label) > - %{ > - <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0;"> > + # expects hash of labels => actions > + def multi_button_popup_footer(buttons = {}) > + buttons_html = "" > + buttons.each{ |label, action| > + buttons_html+="<div class=\"button\">" + > + " <div class=\"button_left_blue\"></div>" + > + " <div class=\"button_middle_blue\"><a href=\"#\" onclick=\"#{action}\">#{label}</a></div>" + > + " <div class=\"button_right_blue\"></div>" + > + "</div>" > + } > + > + %{ > + <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0; float: left; width: 97%;"> > <div class="button"> > <div class="button_left_grey"></div> > <div class="button_middle_grey"><a href="#" onclick="$(document).trigger('close.facebox')">Cancel</a></div> > <div class="button_right_grey"></div> > </div> > - <div class="button"> > - <div class="button_left_blue"></div> > - <div class="button_middle_blue"><a href="#" onclick="#{action}">#{label}</a></div> > - <div class="button_right_blue"></div> > - </div> > + #{buttons_html} > </div> > - } > + } > + end > + > + def popup_footer(action, label) > + multi_button_popup_footer({label => action}) > end > > def ok_footer > diff --git a/src/app/helpers/network_helper.rb b/src/app/helpers/network_helper.rb > new file mode 100644 > index 0000000..ebbce0b > --- /dev/null > +++ b/src/app/helpers/network_helper.rb > @@ -0,0 +1,2 @@ > +module NetworkHelper > +end > diff --git a/src/app/models/bonding.rb b/src/app/models/bonding.rb > index 006c261..a3ad150 100644 > --- a/src/app/models/bonding.rb > +++ b/src/app/models/bonding.rb > @@ -30,6 +30,16 @@ > # interface. They can be ignored if not used. > # > class Bonding < ActiveRecord::Base > + belongs_to :host > + belongs_to :bonding_type > + belongs_to :vlan > + has_many :ip_addresses, :dependent => :destroy > + > + has_and_belongs_to_many :nics, > + :join_table => 'bondings_nics', > + :foreign_key => :bonding_id > + > + > validates_presence_of :name, > :message => 'A name is required.' > > @@ -42,18 +52,17 @@ class Bonding < ActiveRecord::Base > validates_presence_of :interface_name, > :message => 'An interface name is required.' > > - validates_presence_of :boot_type_id, > - :message => 'A boot type must be specified.' > - > validates_presence_of :bonding_type_id, > :message => 'A bonding type must be specified.' > > - belongs_to :host > - belongs_to :bonding_type > - belongs_to :boot_type > + protected > + def validate > + errors.add("name", "must be specified") unless name > + errors.add("interface_name", "must be specified") unless interface_name > + errors.add("bonding_type_id", "must be specified") unless bonding_type_idThis is duplicating the above validation with the validates_presence_of check.> + errors.add("host_id", "must be specified") unless host_id > + errors.add("vlan_id", "must be specified") unless vlan_id > + end > > - has_and_belongs_to_many :nics, > - :join_table => 'bondings_nics', > - :foreign_key => :bonding_id > > end > diff --git a/src/app/models/ip_address.rb b/src/app/models/ip_address.rb > new file mode 100644 > index 0000000..5d2e6af > --- /dev/null > +++ b/src/app/models/ip_address.rb > @@ -0,0 +1,27 @@ > +# ip_address.rb > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Darryl L. Pierce <dpierce at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +# +IpAddress+ is the base class for all address related classes. > +# > +class IpAddress < ActiveRecord::Base > + # one of these 3 will apply for each address > + belongs_to :network > + belongs_to :nic > + belongs_to :bonding > +end > diff --git a/src/app/models/ip_v4_address.rb b/src/app/models/ip_v4_address.rb > new file mode 100644 > index 0000000..40e8cf9 > --- /dev/null > +++ b/src/app/models/ip_v4_address.rb > @@ -0,0 +1,48 @@ > +# ip_v4_address.rb > +# > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Darryl L. Pierce <dpierce at redhat.com>, > +# Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +# +IpV4Address+ represents a single IPv4 address. > +# > +class IpV4Address < IpAddress > + ADDRESS_TEST = %r{^(\d{1,3}\.){3}\d{1,3}$} > + > + validates_presence_of :address, > + :message => 'An address must be supplied.' > + validates_format_of :address, > + :with => ADDRESS_TEST > + > + protected > + def validate > + unless address and address =~ ADDRESS_TEST > + errors.add("address", "is of incorrect format") > + end > + unless !netmask or netmask == "" or netmask =~ ADDRESS_TEST > + errors.add("netmask", "is of incorrect format") > + end > + unless !gateway or gateway == "" or gateway =~ ADDRESS_TEST > + errors.add("gateway", "is of incorrect format") > + end > + unless !broadcast or broadcast == "" or broadcast =~ ADDRESS_TEST > + errors.add("broadcast", "is of incorrect format") > + endIn the original patch I submitted each of these fields was tested using validates_format_of.> + end > + > +end > diff --git a/src/app/models/ip_v6_address.rb b/src/app/models/ip_v6_address.rb > new file mode 100644 > index 0000000..9720179 > --- /dev/null > +++ b/src/app/models/ip_v6_address.rb > @@ -0,0 +1,44 @@ > +# ip_v6_address.rb > +# > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Darryl L. Pierce <dpierce at redhat.com>, > +# Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +# +IpV6Address+ represents a single IPv6 address. > +# > +class IpV6Address < IpAddress > + ADDRESS_TEST = %r{^([0-9a-fA-F]{0,4}|0)(\:([0-9a-fA-F]{0,4}|0)){7}$} > + > + validates_presence_of :address, > + :message => 'An address must be provided.' > + validates_format_of :address, > + :with => ADDRESS_TEST > + > + protected > + def validate > + unless address =~ ADDRESS_TEST > + errors.add("address", "is of incorrect format") > + end > + unless !prefix or prefix == "" or prefix =~ ADDRESS_TEST > + errors.add("prefix", "is of incorrect format") > + end > + unless !gateway or gateway == "" or gateway =~ ADDRESS_TEST > + errors.add("gateway", "is of incorrect format") > + endSee note in the IpV4Address regarding validations. The Rails pattern is to use validates_format_of for checking content.> + end > +end > diff --git a/src/app/models/network.rb b/src/app/models/network.rb > new file mode 100644 > index 0000000..99e1a94 > --- /dev/null > +++ b/src/app/models/network.rb > @@ -0,0 +1,37 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +class Network < ActiveRecord::Base > + belongs_to :boot_type > + has_many :ip_addresses, :dependent => :destroy > + > + has_and_belongs_to_many :usages, :join_table => 'networks_usages' > + > + validates_presence_of :type > + validates_presence_of :name > + validates_presence_of :boot_type_id > + > + def conversion_valid? > + return false if type.to_s == 'Vlan' && > + Vlan.find(id).bondings.size != 0 || > + type.to_s == 'PhysicalNetwork' && > + PhysicalNetwork.find(id).nics.size != 0 > + return true > + end > + > +end > diff --git a/src/app/models/nic.rb b/src/app/models/nic.rb > index baf7095..25d3ac2 100644 > --- a/src/app/models/nic.rb > +++ b/src/app/models/nic.rb > @@ -19,7 +19,19 @@ > > class Nic < ActiveRecord::Base > belongs_to :host > - belongs_to :boot_type > + belongs_to :physical_network > + has_many :ip_addresses, :dependent => :destroy > > - has_and_belongs_to_many :bonding, :join_table => 'bondings_nics' > + has_and_belongs_to_many :bondings, :join_table => 'bondings_nics' > + > + protected > + def validate > + errors.add("host_id", "must be specified") unless host_id > + errors.add("physical_network_id", "must be specified") unless physical_network_idThese should be done with validates_presence_of to keep the code consistent throughout the project. The error message added here is the default from Rails, so need not be specified.> + if physical_network.boot_type.id => + BootType.find(:first, :conditions => { :proto => 'static' }).id andYou can test this without the second DB call using: if physical_network.boot_type.proto == static'> + ip_addresses.size == 0 > + errors.add("ip_address_id", "must be specified") > + end > + end > end> diff --git a/src/app/models/physical_network.rb b/src/app/models/physical_network.rb > new file mode 100644 > index 0000000..cba696a > --- /dev/null > +++ b/src/app/models/physical_network.rb > @@ -0,0 +1,27 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +class PhysicalNetwork < Network > + has_many :nics > + > + protected > + def validate > + errors.add("name", "must be specified") unless name > + errors.add("boot_type_id", "must be specified") unless boot_type_idUse validates_presence_of here.> + end > +end > diff --git a/src/app/models/usage.rb b/src/app/models/usage.rb > new file mode 100644 > index 0000000..353e8f4 > --- /dev/null > +++ b/src/app/models/usage.rb > @@ -0,0 +1,21 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +class Usage < ActiveRecord::Base > + has_and_belongs_to_many :networks, :join_table => 'networks_usages' > +end > diff --git a/src/app/models/vlan.rb b/src/app/models/vlan.rb > new file mode 100644 > index 0000000..2b96502 > --- /dev/null > +++ b/src/app/models/vlan.rb > @@ -0,0 +1,30 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +class Vlan < Network > + has_many :bondings > + > + validates_presence_of :number > + > + protected > + def validate > + errors.add("name", "must be specified") unless name > + errors.add("number", "must be specified") unless number > + errors.add("boot_type_id", "must be specified") unless boot_type_idUse validates_presence_of to be consistent.> + end > +end > diff --git a/src/app/views/dashboard/index.html.erb b/src/app/views/dashboard/index.html.erb > index 92cb4da..8815ebc 100644 > --- a/src/app/views/dashboard/index.html.erb > +++ b/src/app/views/dashboard/index.html.erb > @@ -13,6 +13,13 @@ > </table> > </div> > > + <% if @can_modify %> > + <h3>Networks</h3> > + <a href="<%= url_for :controller => 'network', :action => 'list' %>"> > + View / Edit > + </a> > + <% end %> > + > </div> <!-- end #tools --> > </td> > > diff --git a/src/app/views/host/edit_network.rhtml b/src/app/views/host/edit_network.rhtml > new file mode 100644 > index 0000000..7ec3180 > --- /dev/null > +++ b/src/app/views/host/edit_network.rhtml > @@ -0,0 +1,85 @@ > +<%- content_for :title do -%> > + Edit <%= @host.hostname %> Network Devices > +<%- end -%> > + > +<%- content_for :description do -%> > + Select and edit nics and bonded interfaces on <%= @host.hostname %> > +<%- end -%> > + > +<div id="select-host-nic" class="popup-content-selection"> > +<%= select_with_label "NICs", "nic", "id", > + @host.nics. > + collect{ |nic| [nic.mac, nic.id] }. > + insert(0, "") %> > +</div> > + > +<div id="select-host-bonding" class="popup-content-selection"> > +<%= select_with_label "Bonded Interfaces", "bonding", "id", [] %> > +</div> > + > +<div style="clear: both;"></div> > + > +<div id="selected_nic_bonding" class="selected_popup_content"></div> > + > +<div id="host_network_footer" class="popup-content-footer"> > + <%= ok_footer %> > +</div> > + > +<script type="text/javascript"> > +function reset_nics_bonding_detail(){ > + var data='Select NIC or Bonded Interface<br/>'; > + > + $("#selected_nic_bonding").html(data); > + $("#host_network_footer").show(); > +}; > + > +reset_nics_bonding_detail(); // run it once for inital content > + > +function reset_nics_select(){ > + $("#nic_id option:first").attr("selected", true); > +}; > + > +function reset_bonding_select(){ > + // incase of new additions / deletions, repopulate select box > + $.getJSON( > + "<%= url_for :action => 'bondings_json', :id => @host.id %>", > + {}, > + function(j){ > + var options = "<option value=''></option>" + > + "<option value='New'>New</option>"; > + for(var i = 0; i < j.length; i++){ > + options += '<option value="' + j[i].id + '">' + j[i].name + > + '</option>'; > + } > + $("#bonding_id").html(options); > + }); > + > + $("#bonding_id option:first").attr("selected", true); > +}; > + > +reset_bonding_select(); // run it once for initial content > + > +$("#nic_id").change(function () { > + reset_bonding_select(); > + if($('#nic_id').val() != ""){ > + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', > + :action => 'edit_nic'%>/" + $('#nic_id').val()); > + $("#host_network_footer").hide(); > + }else{ > + reset_nics_bonding_detail(); > + } > +}); > + > +$("#bonding_id").change(function () { > + reset_nics_select(); > + if($('#bonding_id').val() == "New"){ > + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'new_bonding', :host_id => @host.id %>"); > + $("#host_network_footer").hide(); > + }else if($('#bonding_id').val() != ""){ > + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'edit_bonding'%>/" + $('#bonding_id').val()); > + $("#host_network_footer").hide(); > + }else{ > + reset_nics_bonding_detail(); > + } > +}); > +</script> > diff --git a/src/app/views/host/show.rhtml b/src/app/views/host/show.rhtml > index d1b05ad..bc39a62 100644 > --- a/src/app/views/host/show.rhtml > +++ b/src/app/views/host/show.rhtml > @@ -17,6 +17,10 @@ > <%= image_tag "icon_x.png" %> Clear VMs > </a> > <% end -%> > + <%= link_to image_tag("icon_edit.png") +"Edit Network", > + {:controller => 'host', > + :action => 'edit_network', :id => @host.id}, > + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> > <%- end -%> > <%- end -%> > <script type="text/javascript"> > diff --git a/src/app/views/network/_bonding_form.rhtml b/src/app/views/network/_bonding_form.rhtml > new file mode 100644 > index 0000000..4ec0df1 > --- /dev/null > +++ b/src/app/views/network/_bonding_form.rhtml > @@ -0,0 +1,46 @@ > + <%= error_messages_for 'bonding' %> > + <%= error_messages_for 'ip_address' %> > + > +<div id="selected_popup_content_expanded" class="dialog_form"> > + <%= hidden_field_tag 'bonding[host_id]', @host.id %> > + > + <div class="selected_popup_content_left">Name</div> > + <div class="selected_popup_content_right"> > + <%= text_field_with_label "", "bonding", "name" %> > + </div> > + > + <div class="selected_popup_content_left">Interface Name</div> > + <div class="selected_popup_content_right"> > + <%= text_field_with_label "", "bonding", "interface_name" %> > + </div> > + > + <div class="selected_popup_content_left">Bonding Type</div> > + <div class="selected_popup_content_right"> > + <%= select "bonding", "bonding_type_id", > + @bonding_types.collect { |bt| [bt.label, bt.id ] } %> > + </div> > + > + <% if @host.nics.size != 0 %> > + <div class="selected_popup_content_left">NICs</div> > + <div class="selected_popup_content_right"> > + <select id="bonding_nic_ids" name="bonding[nic_ids][]" multiple="true"> > + <%= options_from_collection_for_select @host.nics, "id", "mac", > + @bonding ? @bonding.nics.collect{ |x| x.nic_id.to_i } : [] %> > + </select> > + </div> > + <% end %> > + > + <%= render :partial => 'select', :locals => { :target => 'bonding' } %> > + > +<div id="static_ip_options" > + style="<% unless ((@network && @network.boot_type_id == @static_boot_type.id) || > + (!@network && @networks.size > 0 && > + @networks[0].boot_type_id == @static_boot_type.id)) %> > + display: none;<%end %>"> > + <%= render :partial => 'ip_addresses_form', > + :locals => { :parent_type => 'bonding', > + :parent_id => @bonding ? @bonding.id : nil } %> > +</div> > + > + > +</div> > diff --git a/src/app/views/network/_form.rhtml b/src/app/views/network/_form.rhtml > new file mode 100644 > index 0000000..3f393f7 > --- /dev/null > +++ b/src/app/views/network/_form.rhtml > @@ -0,0 +1,31 @@ > +<%= error_messages_for 'network' %> > + > +<!--[form:network]--> > +<%= hidden_field 'network', 'id' if @create %> > + > +<%= text_field_with_label "Name:", "network", "name", > + {:style=>"width:250px;"} %> > + > +<%= select_with_label "Boot Type:", 'network', 'boot_type_id', > + @boot_types.collect{ |bt| [bt.label, bt.id] }, > + :style=>"width:250px;" %> > + > +<%= select_with_label "Type", "network", "type", > + [[ "Physical Network", "PhysicalNetwork" ], > + [ "VLAN", "Vlan" ] ] %> > + > +<div id="vlan_options" style="display: none;"> > +<%= text_field_with_label "Number:", "network", "number", > + {:style=>"width:250px;"} %> > +</div> > + > + > +<script type="text/javascript"> > +$("#network_type").change(function () { > + if($('#network_type').val() == "Vlan"){ > + $("#vlan_options").show(); > + }else{ > + $("#vlan_options").hide(); > + } > +}).trigger('change'); > +</script> > diff --git a/src/app/views/network/_grid.rhtml b/src/app/views/network/_grid.rhtml > new file mode 100644 > index 0000000..6af0c05 > --- /dev/null > +++ b/src/app/views/network/_grid.rhtml > @@ -0,0 +1,37 @@ > +<% networks_per_page = 40 unless (defined? networks_per_page) and !(networks_per_page.nil?) %> > +<% usepager = @networks.size > networks_per_page %> > + > +<div id="<%= table_id %>_div"> > +<form id="<%= table_id %>_form"> > +<table id="<%= table_id %>" style="display:none"></table> > +</form> > +</div> > +<script type="text/javascript"> > + $("#<%= table_id %>").flexigrid > + ( > + { > + url: '<%= url_for :action => "networks_json" %>', > + dataType: 'json', > + colModel : [ > + {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, > + {display: 'Name', name: 'name', width : 180, align: 'left'}, > + {display: 'Type', name: 'type', width : 180, align: 'left'}, > + {display: 'Boot Type', name: 'boot_type', width : 180, align: 'left'} > + ], > + sortname: "name", > + sortorder: "asc", > + usepager: <%= usepager %>, > + useRp: <%= usepager %>, > + rp: <%= networks_per_page %>, > + showTableToggleBtn: true, > + onSelect: <%= on_select %> > + } > + ); > + function <%= table_id %>checkbox(celDiv) > + { > + $(celDiv).html('<input type="checkbox" name="grid_checkbox'+$(celDiv).html()+'" class="grid_checkbox" value="'+$(celDiv).html()+'"/>'); > + } > + > +</script> > + > + > diff --git a/src/app/views/network/_ip_address_form.rhtml b/src/app/views/network/_ip_address_form.rhtml > new file mode 100644 > index 0000000..b952807 > --- /dev/null > +++ b/src/app/views/network/_ip_address_form.rhtml > @@ -0,0 +1,62 @@ > +<%= error_messages_for 'ip_address' %> > + > +<div id="selected_popup_content_expanded" class="dialog_form"> > + <%= hidden_field_tag 'parent_type', @parent_type%> > + <%= hidden_field_tag 'ip_address[id]', @ip_address.id if @ip_address%> > + <%= hidden_field_tag 'ip_address[network_id]', @network.id if @network %> > + <%= hidden_field_tag 'ip_address[nic_id]', @nic.id if @nic %> > + <%= hidden_field_tag 'ip_address[bonding_id]', @bonding.id if @bonding %> > + > + <div class="selected_nic_bonding_left">Type:</div> > + <div class="selected_nic_bonding_right"> > + <%= select_with_label "", "ip_address", "type", > + [[ "ipv4", "IpV4Address" ], [ "ipv6", "IpV6Address" ] ] %> > + </div> > + > + > + <div class="static_ip_common_options"> > + <div class="selected_nic_bonding_left">IP Address</div> > + <div class="selected_nic_bonding_right"> > + <%= text_field_with_label "", "ip_address", "address" %> > + </div> > + </div> > + > + <div id="static_ip_v4_options"> > + <div class="selected_nic_bonding_left">Netmask</div> > + <div class="selected_nic_bonding_right"> > + <%= text_field_with_label "", "ip_address", "netmask" %> > + </div> > + > + <div class="selected_nic_bonding_left">Broadcast</div> > + <div class="selected_nic_bonding_right"> > + <%= text_field_with_label "", "ip_address", "broadcast" %> > + </div> > + </div> > + > + <div id="static_ip_v6_options" style="display: none;"> > + <div class="selected_nic_bonding_left">Prefix</div> > + <div class="selected_nic_bonding_right"> > + <%= text_field_with_label "", "ip_address", "prefix" %> > + </div> > + </div> > + > + <div class="static_ip_common_options"> > + <div class="selected_nic_bonding_left">Gateway</div> > + <div class="selected_nic_bonding_right"> > + <%= text_field_with_label "", "ip_address", "gateway" %> > + </div> > + </div> > + > +</div> > + > +<script type="text/javascript"> > + $("#ip_address_type").change(function () { > + if($("#ip_address_type").val() == 'IpV4Address'){ > + $("#static_ip_v4_options").show(); > + $("#static_ip_v6_options").hide(); > + }else{ > + $("#static_ip_v4_options").hide(); > + $("#static_ip_v6_options").show(); > + } > + }).trigger('change'); > +</script> > diff --git a/src/app/views/network/_ip_addresses_form.rhtml b/src/app/views/network/_ip_addresses_form.rhtml > new file mode 100644 > index 0000000..f833b2a > --- /dev/null > +++ b/src/app/views/network/_ip_addresses_form.rhtml > @@ -0,0 +1,50 @@ > +<div id="select_ip_address" class="popup-content-selection"> > +<%= select_with_label "IP Addresses", "ip_address", "id", [] %> > +</div> > + > +<div id="selected_ip_address" class="selected_popup_content"></div> > + > +<script type="text/javascript"> > +function reset_selected_ip_address(){ > + var data='Select IP Address<br/>'; > + > + $("#selected_ip_address").html(data); > + $("#ip_addresses_footer").show(); > +}; > + > +reset_selected_ip_address(); // run it once for inital content > + > +function reset_ip_address_select(){ > + // incase of new additions / deletions, repopulate select box > + $.getJSON( > + "<%= url_for :action => 'ip_addresses_json', :id => parent_id, :parent_type => parent_type %>", > + {}, > + function(j){ > + var options = "<option value=''></option>" + > + "<option value='New'>New</option>"; > + for(var i = 0; i < j.length; i++){ > + options += '<option value="' + j[i].id + '">' + j[i].name + > + '</option>'; > + } > + $("#ip_address_id").html(options); > + }); > + > + $("#ip_address_id option:first").attr("selected", true); > +}; > + > +reset_ip_address_select(); // run it once for initial content > + > +$("#ip_address_id").change(function () { > + if($('#ip_address_id').val() == "New"){ > + $("#selected_ip_address").load("<%= url_for :action => 'new_ip_address', :id => parent_id, :parent_type => parent_type %>"); > + $("#ip_addresses_footer").hide(); > + }else if($('#ip_address_id').val() != ""){ > + $("#selected_ip_address").load("<%= url_for :action => 'edit_ip_address'%>/" + $('#ip_address_id').val() + "?parent_type=<%= parent_type %>"); > + $("#ip_addresses_footer").hide(); > + }else{ > + reset_selected_ip_address(); > + } > +}); > + > +</script> > + > diff --git a/src/app/views/network/_select.rhtml b/src/app/views/network/_select.rhtml > new file mode 100644 > index 0000000..e69d9b0 > --- /dev/null > +++ b/src/app/views/network/_select.rhtml > @@ -0,0 +1,32 @@ > +<% target = 'nic' unless target > + network_id = 'physical_network_id' if target == 'nic' > + network_id = 'vlan_id' if target == 'bonding' > + > + %> > + > +<div class="selected_popup_content_left">Network:</div> > +<div class="selected_popup_content_right"> > + <%= select_with_label "", target, network_id, > + @networks.collect { |n| [n.name + ' - ' + n.boot_type.label, n.id ] } %> > + > +</div> > + > +<script type="text/javascript"> > + var static_network_ids=<%= static_network_ids = '[' > + @networks.each { |n| > + static_network_ids += ',' if static_network_ids != '[' > + static_network_ids += n.id.to_s if n.boot_type_id == @static_boot_type.id > + } > + static_network_ids += ']' > + static_network_ids %>; > + > + $("#<%=target %>_<%= network_id %>").change(function () { > + $("#static_ip_options").hide(); > + for(i=0; i<static_network_ids.length; ++i){ > + if($("#<%= target %>_<%= network_id %>").val() == static_network_ids[i]){ > + $("#static_ip_options").show(); > + break; > + } > + }; > + }); > +</script> > diff --git a/src/app/views/network/edit.rhtml b/src/app/views/network/edit.rhtml > new file mode 100644 > index 0000000..6360b3f > --- /dev/null > +++ b/src/app/views/network/edit.rhtml > @@ -0,0 +1,34 @@ > +<%- content_for :title do -%> > + Edit Network > +<%- end -%> > +<%- content_for :description do -%> > +<%- end -%> > + > + <!-- DIALOG BODY --> > + <form method="POST" action="<%= url_for :action => 'update' %>" id="network_form" > > + <div class="dialog_form"> > + <%= hidden_field_tag 'id', @network.id %> > + <%= render :partial => 'form', :locals => { :create => false } %> > + </div> > + <!-- DIALOG FOOTER --> > + <%= popup_footer("$('#network_form').submit()", "Edit Network") %> > + </form> > + > +<script type="text/javascript"> > +$(function() { > + var networkoptions = { > + target: '<%= url_for :action => 'update' %>', > + dataType: 'json', > + success: function(response, status) { > + afterNetwork(response, status); > + refresh_summary_static('networks_selection', > + '<div class="selection_left"><div>Select a network.</div></div>'); > + } > + }; > + > + // bind form using 'ajaxForm' > + $('#network_form').ajaxForm(networkoptions); > +}); > +</script> > + > + > diff --git a/src/app/views/network/edit_bonding.rhtml b/src/app/views/network/edit_bonding.rhtml > new file mode 100644 > index 0000000..645e469 > --- /dev/null > +++ b/src/app/views/network/edit_bonding.rhtml > @@ -0,0 +1,52 @@ > +<form method="POST" > + action="<%= url_for :action => 'update_bonding' %>" id="edit_bonding_form" > > + <div id="selected_popup_content_header"> > + Editing Bonded Interface > + </div> > + > + <%= hidden_field_tag('id', @bonding.id) %> > + <%= render :partial => 'bonding_form' %> > + > +</form> > + > +<form method="POST" action="<%= url_for :action => 'destroy_bonding' %>" id="delete_bonding_form" > > + <%= hidden_field_tag('id', @bonding.id) %> > +</form> > + > +<%= multi_button_popup_footer({"Edit Bonding" => > + "$('#edit_bonding_form').submit()", > + "Delete Bonding" => > + "$('#delete_bonding_form').submit()"}) %> > + > +<script type="text/javascript"> > +$(function() { > + var edit_bonding_options = { > + target: '<%= url_for :action => 'update_bonding' %>', > + dataType: 'json', > + success: function(response, status) { > + ajax_validation(response, status); > + if (response.success) { > + reset_bonding_select(); > + reset_nics_bonding_detail(); > + } > + } > + }; > + > + var delete_bonding_options = { > + target: '<%= url_for :action => 'destroy_bonding' %>', > + dataType: 'json', > + success: function(response, status) { > + ajax_validation(response, status); > + if (response.success) { > + reset_bonding_select(); > + reset_nics_bonding_detail(); > + } > + } > + }; > + > + // bind forms using 'ajaxForm' > + $('#edit_bonding_form').ajaxForm(edit_bonding_options); > + $('#delete_bonding_form').ajaxForm(delete_bonding_options); > + > +}); > +</script> > diff --git a/src/app/views/network/edit_ip_address.rhtml b/src/app/views/network/edit_ip_address.rhtml > new file mode 100644 > index 0000000..5a3cc18 > --- /dev/null > +++ b/src/app/views/network/edit_ip_address.rhtml > @@ -0,0 +1,66 @@ > +<% if @parent_type == 'network' %> > +<form method="POST" > + action="<%= url_for :action => 'update_ip_address' %>" > + id="edit_ip_address_form" > > +<% end %> > + > + <div id="selected_popup_content_header"> > + Editing IP Address > + </div> > + > + <% if @parent_type != 'network' %> > + <a href="#" onClick="$('#delete_ip_address_form').submit()" style="color: blue;">Delete</a> > + <% end %> > + > + <%= hidden_field_tag('id', @ip_address.id) %> > + <%= render :partial => 'ip_address_form' %> > + > +<% if @parent_type == 'network' %> > +</form> > +<% end %> > + > +<form method="POST" > + action="<%= url_for :action => 'destroy_ip_address' %>" > + id="delete_ip_address_form" > > + <%= hidden_field_tag('id', @ip_address.id) %> > +</form> > + > +<% if @parent_type == 'network' %> > +<%= multi_button_popup_footer({" Edit IP Address" => > + "$('#edit_ip_address_form').submit()", > + "Delete IP Address" => > + "$('#delete_ip_address_form').submit()"}) %> > +<% end %> > + > +<script type="text/javascript"> > +$(function() { > + var edit_ip_address_options = { > + target: '<%= url_for :action => 'update_ip_address' %>', // target element to update > + dataType: 'json', > + success: function(response, status) { > + ajax_validation(response, status); > + if (response.success) { > + reset_selected_ip_address(); > + reset_ip_address_select(); > + } > + } > + }; > + > + var delete_ip_address_options = { > + target: '<%= url_for :action => 'delete_ip_address' %>', // target element to update > + dataType: 'json', > + success: function(response, status) { > + ajax_validation(response, status); > + if (response.success) { > + reset_selected_ip_address(); > + reset_ip_address_select(); > + } > + } > + }; > + > + // bind forms using 'ajaxForm' > + $('#edit_ip_address_form').ajaxForm(edit_ip_address_options); > + $('#delete_ip_address_form').ajaxForm(delete_ip_address_options); > + > +}); > +</script> > diff --git a/src/app/views/network/edit_network_ip_addresses.rhtml b/src/app/views/network/edit_network_ip_addresses.rhtml > new file mode 100644 > index 0000000..7a1e4cb > --- /dev/null > +++ b/src/app/views/network/edit_network_ip_addresses.rhtml > @@ -0,0 +1,13 @@ > +<%- content_for :title do -%> > + Edit Network IP Addresses > +<%- end -%> > +<%- content_for :description do -%> > +<%- end -%> > + > +<%= render :partial => 'ip_addresses_form', > + :locals => { :parent_type => 'network', > + :parent_id => @network.id } %> > + > +<div id="ip_addresses_footer" class="popup-content-footer"> > + <%= ok_footer %> > +</div> > diff --git a/src/app/views/network/edit_nic.rhtml b/src/app/views/network/edit_nic.rhtml > new file mode 100644 > index 0000000..75af6fb > --- /dev/null > +++ b/src/app/views/network/edit_nic.rhtml > @@ -0,0 +1,62 @@ > +<form method="POST" > + action="<%= url_for :action => 'update_nic' %>" id="nic_form" > > + <div id="selected_popup_content_header"> > + Editing NIC > + </div> > + > + <%= error_messages_for 'nic' %> > + > + <div id="selected_popup_content_expanded" class="dialog_form"> > + <%= hidden_field_tag 'id', @nic.id %> > + <%= hidden_field_tag 'nic_host_id', @nic.host.id %> > + <%= hidden_field_tag 'nic_network_id', @nic.physical_network.id %> > + > + <div class="selected_popup_content_left">MAC:</div> > + <div class="selected_popup_content_right"><%= @nic.mac %></div> > + > + <% if @nic.host.bondings.size != 0 %> > + <div class="selected_popup_content_left">Bonded Interfaces</div> > + <div class="selected_popup_content_right"> > + <select id="nic_bonding_ids" name="nic[bonding_ids][]"> > + <option value="" /> > + <%= options_from_collection_for_select @nic.host.bondings, > + "id", "name", > + @nic.bondings.size > 0 ? @nic.bondings[0].bonding_id.to_i : "" %> > + </select> > + </div> > + <% end %> > + > + > + <%= render :partial => 'select' %> > + > + <div id="static_ip_options" > + style="<% if @network.boot_type_id != @static_boot_type.id %> > + display: none;<%end %>"> > + <%= render :partial => 'ip_addresses_form', > + :locals => { :parent_type => 'nic', > + :parent_id => @nic.id } %> > + </div> > + > + > + </div> > + <%= popup_footer("$('#nic_form').submit()", "Edit NIC") %> > +</form> > + > +<script type="text/javascript"> > +$(function() { > + var nicoptions = { > + target: '<%= url_for :action => 'update_nic' %>', > + dataType: 'json', > + success: function(response, status) { > + ajax_validation(response, status); > + if (response.success) { > + reset_nics_select(); > + reset_nics_bonding_detail(); > + } > + } > + }; > + > + // bind form using 'ajaxForm' > + $('#nic_form').ajaxForm(nicoptions); > +}); > +</script> > diff --git a/src/app/views/network/list.html.erb b/src/app/views/network/list.html.erb > new file mode 100644 > index 0000000..9a304cf > --- /dev/null > +++ b/src/app/views/network/list.html.erb > @@ -0,0 +1,72 @@ > +<div id="toolbar_nav"> > +<ul> > + <li><a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]"><%= image_tag "icon_addstorage.png", :style => "vertical-align:middle;" %> Add Network</a></li> > + <li> > + <a href="#" onClick="delete_networks();" > > + <%= image_tag "icon_remove.png", :style=>"vertical-align:middle;" %> > + Remove > + </a> > + </li> > + </ul> > +</div> > + > +<script type="text/javascript"> > + function delete_networks(){ > + var networks = get_selected_networks(); > + if (validate_selected(get_selected_networks(), 'networks')) { > + $.post('<%= url_for :action => 'delete' %>', > + { network_ids: networks.toString() }, > + function(data,status){ > + if (data.alert) { > + $.jGrowl(data.alert); > + } > + grid = $("#networks_grid"); > + if (grid.size()>0 && grid != null) { > + grid.flexReload(); > + } > + empty_summary('networks_selection', 'Network'); > + }, 'json'); > + } > + } > + > + function networks_select(selected_rows) > + { > + var selected_ids = new Array(); > + for(i=0; i<selected_rows.length; i++) { > + selected_ids[i] = selected_rows[i].id; > + } > + if (selected_ids.length == 1) > + { > + $('#networks_selection').load('<%= url_for :action => "show" %>', > + { id: parseInt(selected_ids[0].substring(3))}); > + } > + } > + > +</script> > + > +<div class="panel_header"></div> > +<% if @networks.size != 0 %> > + <div class="data_section"> > + <%= render :partial => "grid", :locals => { :table_id => "networks_grid", > + :networks => @networks, > + :on_select => "networks_select"} %> > + </div> > + > + <div class="selection_detail" id="networks_selection"> > + <div class="selection_left"> > + <div>Select a network.</div> > + </div> > + </div> > +<% else %> > + <div class="data_section"> > + <div class="no-grid-items"> > + <%= image_tag 'no-grid-items.png', :style => 'float: left;' %> > + > + <div class="no-grid-items-text"> > + No networks found. <br/><br/> > + <%= image_tag "icon_addhost.png", :style=>"vertical-align:middle;" %> > + <a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]">Add first network</a> > + </div> > + </div> > + </div> > +<% end %> > diff --git a/src/app/views/network/new.rhtml b/src/app/views/network/new.rhtml > new file mode 100644 > index 0000000..15b7304 > --- /dev/null > +++ b/src/app/views/network/new.rhtml > @@ -0,0 +1,28 @@ > +<%- content_for :title do -%> > + Add Network > +<%- end -%> > +<%- content_for :description do -%> > +<%- end -%> > + > + <!-- DIALOG BODY --> > + <form method="POST" action="<%= url_for :action => 'create' %>" id="network_form" > > + <div class="dialog_form"> > + <%= render :partial => 'form', :locals => { :create => true } %> > + </div> > + <!-- DIALOG FOOTER --> > + <%= popup_footer("$('#network_form').submit()", "Add Network") %> > + </form> > + > +<script type="text/javascript"> > +$(function() { > + var networkoptions = { > + target: '<%= url_for :action => 'create' %>', > + dataType: 'json', > + success: afterNetwork // post-submit callback > + }; > + > + // bind form using 'ajaxForm' > + $('#network_form').ajaxForm(networkoptions); > +}); > +</script> > + > diff --git a/src/app/views/network/new_bonding.rhtml b/src/app/views/network/new_bonding.rhtml > new file mode 100644 > index 0000000..9ee6994 > --- /dev/null > +++ b/src/app/views/network/new_bonding.rhtml > @@ -0,0 +1,32 @@ > +<form method="POST" > + action="<%= url_for :action => 'create_bonding' %>" id="bonding_form" > > + > + <div id="selected_popup_content_header"> > + Create Bonded Interface > + </div> > + > + <%= render :partial => 'bonding_form' %> > +</form> > + > +<%= multi_button_popup_footer({"Create Bonding" => > + "$('#bonding_form').submit()"}) %> > + > +<script type="text/javascript"> > +$(function() { > + var bonding_options = { > + target: '<%= url_for :action => 'create_bonding' %>', > + dataType: 'json', > + success: function(response, status) { > + ajax_validation(response, status); > + if (response.success) { > + reset_bonding_select(); > + reset_nics_bonding_detail(); > + } > + } > + }; > + > + // bind forms using 'ajaxForm' > + $('#bonding_form').ajaxForm(bonding_options); > + > +}); > +</script> > diff --git a/src/app/views/network/new_ip_address.rhtml b/src/app/views/network/new_ip_address.rhtml > new file mode 100644 > index 0000000..6167a76 > --- /dev/null > +++ b/src/app/views/network/new_ip_address.rhtml > @@ -0,0 +1,33 @@ > +<% if @parent_type == 'network' %> > +<form method="POST" > + action="<%= url_for :action => 'create_ip_address' %>" > + id="ip_address_form" > > +<% end %> > + > + <div id="selected_popup_content_header"> > + Create Ip Address > + </div> > + > + <%= render :partial => 'ip_address_form' %> > + > +<% if @parent_type == 'network' %> > +</form> > +<% end %> > + > +<% if @parent_type == 'network' %> > +<%= multi_button_popup_footer({"Create IP Address" => > + "$('#ip_address_form').submit()"}) %> > +<% end %> > + > +<script type="text/javascript"> > +$(function() { > + var ip_address_options = { > + target: '<%= url_for :action => 'create_ip_address' %>', > + dataType: 'json', > + success: afterIpAddress > + }; > + > + // bind forms using 'ajaxForm' > + $('#ip_address_form').ajaxForm(ip_address_options); > +}); > +</script> > diff --git a/src/app/views/network/show.rhtml b/src/app/views/network/show.rhtml > new file mode 100644 > index 0000000..a56c41d > --- /dev/null > +++ b/src/app/views/network/show.rhtml > @@ -0,0 +1,30 @@ > +<%- content_for :title do -%> > + <%=h @network.name %> > +<%- end -%> > + > +<%- content_for :action_links do -%> > + <%= link_to image_tag("icon_edit.png") + "Edit", > + {:action => 'edit', :id => @network.id }, > + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> > + > + <%= link_to image_tag("icon_edit.png") + "Edit IP Addresses", > + {:action => 'edit_network_ip_addresses', :id => @network.id }, > + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> > +<%- end -%> > + > +<div id="hosts_selection_id" style="display:none"><%= @host.id %></div> > +<div class="selection_key"> > + Name:<br/> > + Type:<br/> > + Boot Type:<br/> > + IP Addresses:<br/> > +</div> > +<div class="selection_value"> > + <%=h @network.name %><br/> > + <%=h @network.type %><br/> > + <%=h @network.boot_type.label %><br/> > + <%=h @network.ip_addresses.size %><br/> > +</div> > + > +<%- content_for :right do -%> > +<%- end -%> > diff --git a/src/app/views/nic/_list.rhtml b/src/app/views/nic/_list.rhtml > index 07f7e44..eb3a4a7 100644 > --- a/src/app/views/nic/_list.rhtml > +++ b/src/app/views/nic/_list.rhtml > @@ -1,7 +1,6 @@ > <table class='listing'> > <thead> > <tr> > - <th class="empty">IP</th> > <th>MAC</th> > <th>Bridge</th> > <th>Usage Type</th> > @@ -11,7 +10,6 @@ > </thead> > <% for nic in nics %> > <tr class="<%= cycle('odd','even', :name => nics) %>"> > - <td><%= link_to nic.ip_addr, { :controller => "nic", :action => 'show', :id => nic }, { :class => "show" } %></td> > <td><%= nic.mac %></td> > <td><%= nic.bridge %></td> > <td><%= nic.usage_type %></td> > diff --git a/src/db/migrate/028_refactor_networking_model.rb b/src/db/migrate/028_refactor_networking_model.rb > new file mode 100644 > index 0000000..946b976 > --- /dev/null > +++ b/src/db/migrate/028_refactor_networking_model.rb > @@ -0,0 +1,271 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +# introduce networks and ip_addresses tables, refactor relationships > +class RefactorNetworkingModel < ActiveRecord::Migration > + def self.up > + > + #################################################### > + # bugfix, bridge tables shouldn't have their own ids > + remove_column :bondings_nics, :id > + > + ################################################################## > + # add networks, usages tables and networks_usage_types bridge > + create_table :networks do |t| > + t.string :type, :null => false > + t.string :name, :null => false > + t.integer :boot_type_id, :null => false > + > + # attributes for Vlan (type=Vlan) > + t.integer :number > + end > + > + create_table :usages do |t| > + t.string :label, :null => false > + t.string :usage, :null => false > + end > + > + create_table :networks_usages, :id => false do |t| > + t.integer :network_id, :null => false > + t.integer :usage_id, :null => false > + end > + > + add_index :networks_usages, [:network_id, :usage_id], :unique => true > + > + # create usages > + Usage.create(:label => 'Guest', :usage => 'guest') > + Usage.create(:label => 'Management', :usage => 'management') > + Usage.create(:label => 'Storage', :usage => 'storage') > + > + # referential integrity for networks tables > + execute "alter table networks add constraint > + fk_network_boot_types > + foreign key (boot_type_id) references > + boot_types(id)" > + execute "alter table networks_usages add constraint > + fk_networks_usages_network_id > + foreign key (network_id) references > + networks(id)" > + execute "alter table networks_usages add constraint > + fk_networks_usages_usage_id > + foreign key (usage_id) references > + usages(id)" > + > + # add foreign keys to nics / bondings table > + add_column :nics, :physical_network_id, :integer > + add_column :bondings, :vlan_id, :integer > + > + # referential integrity for nic/bondings network ids > + execute "alter table nics add constraint > + fk_nic_networks > + foreign key (physical_network_id) references > + networks(id)" > + execute "alter table bondings add constraint > + fk_bonding_networks > + foreign key (vlan_id) references > + networks(id)" > + > + #################################################### > + # create ip_addresses table > + create_table :ip_addresses do |t| > + t.string :type > + > + # foreign keys to associated entities > + t.integer :nic_id > + t.integer :bonding_id > + t.integer :network_id > + > + # common attributes > + t.string :address, :limit => 39, :null => false > + t.string :gateway, :limit => 39 > + > + # attributes for IPv4 (type=IpV4Address) > + t.string :netmask, :limit => 15 > + t.string :broadcast, :limit => 15 > + > + # attributes for IPv6 (type=IpV6Address) > + t.string :prefix, :limit => 39 > + t.timestamps > + end > + > + # referential integrity for ip_addresses table > + execute "alter table ip_addresses add constraint > + fk_nic_ip_addresses > + foreign key (nic_id) references nics(id)" > + execute "alter table ip_addresses add constraint > + fk_bonding_ip_addresses > + foreign key (bonding_id) references bondings(id)" > + execute "alter table ip_addresses add constraint > + fk_network_ip_addresses > + foreign key (network_id) references networks(id)" > + > + ################################################################### > + static_boot_type_id > + BootType.find(:first, > + :conditions => {:proto => 'static'} ).id > + > + # migrate nic ip_addresses to networks / ip_addresses table > + i = 0 > + Nic.find(:all).each do |nic| > + if nic.boot_type_id == static_boot_type_id > + IpV4Address.new(:nic_id => nic.id, > + :address => nic.ip_addr).save! > + > + end > + network = PhysicalNetwork.new( > + :name => 'Physical Network ' + i.to_s, > + :boot_type_id => nic.boot_type_id) > + network.save! > + > + ip_address = IpV4Address.new(:address => nic.ip_addr ? nic.ip_addr : '0.0.0.0', > + :netmask => nic.netmask, > + :broadcast => nic.broadcast, > + :gateway => nic.ip_addr) > + ip_address.network = network > + ip_address.save! > + > + nic.physical_network = network > + nic.save! > + > + i += 1 > + end > + > + # migrate bonding ip_addresses to networks / ip_addresses table > + i = 0 > + Bonding.find(:all).each do |bonding| > + if bonding.boot_type_id == static_boot_type_id > + IpV4Address.new(:bonding_id => bonding.id, > + :address => bonding.ip_addr).save! > + end > + network = Vlan.new( > + :name => 'VLAN ' + i.to_s, > + :number => i, > + :boot_type_id => bonding.boot_type_id) > + network.save! > + > + ip_address = IpV4Address.new(:address => bonding.ip_addr ? bonding.ip_addr : '0.0.0.0', > + :netmask => bonding.netmask, > + :broadcast => bonding.broadcast, > + :gateway => bonding.ip_addr) > + ip_address.network = network > + ip_address.save! > + > + bonding.vlan = network > + bonding.save! > + > + i += 1 > + end > + > + ############################################################## > + # remove nics / bonding ip address and network related columns > + remove_column :nics, :ip_addr > + remove_column :nics, :netmask > + remove_column :nics, :broadcast > + remove_column :nics, :boot_type_id > + remove_column :bondings, :ip_addr > + remove_column :bondings, :netmask > + remove_column :bondings, :broadcast > + remove_column :bondings, :boot_type_id > + > + > + end > + > + def self.down > + ############################################################## > + # readd nics / bonding ip address related columns > + add_column :nics, :ip_addr, :string, :limit => 16 > + add_column :nics, :netmask, :string, :limit => 16 > + add_column :nics, :broadcast, :string, :limit => 16 > + add_column :nics, :boot_type_id, :integer > + add_column :bondings, :ip_addr, :string, :limit => 16 > + add_column :bondings, :netmask, :string, :limit => 16 > + add_column :bondings, :broadcast, :string, :limit => 16 > + add_column :bondings, :boot_type_id, :integer > + > + execute "alter table nics add constraint > + fk_nic_boot_types > + foreign key (boot_type_id) references > + boot_types(id)" > + execute "alter table bondings add constraint > + fk_bonding_boot_types > + foreign key (boot_type_id) references > + boot_types(id)" > + > + ############################################################## > + # attempt to migrate ip information back into nics table. > + # because a nic can have multiple ips (if statically > + # assigned) as well as its network, just use the 1st > + # found > + Nic.find(:all).each do |nic| > + if nic.physical_network.ip_addresses.size > 0 > + # use the 1st configured network ip > + nic.ip_addr = nic.physical_network.ip_addresses[0].address > + nic.netmask = nic.physical_network.ip_addresses[0].netmask > + nic.broadcast = nic.physical_network.ip_addresses[0].broadcast > + end > + > + if nic.ip_addresses.size > 0 > + # use the 1st assigned static ip > + nic.ip_addr = nic.ip_addresses[0].address > + end > + > + nic.boot_type_id = nic.physical_network.boot_type_id > + > + nic.save! > + end > + > + # attempt to migrate ip information back into bondings table. > + # because a bonding can have multiple ips (if statically > + # assigned) as well as its network, just use the 1st > + # found > + Bonding.find(:all).each do |bonding| > + if bonding.vlan.ip_addresses.size > 0 > + # use the 1st configured network ip > + bonding.ip_addr = bonding.vlan.ip_addresses[0].address > + bonding.netmask = bonding.vlan.ip_addresses[0].netmask > + bonding.broadcast = bonding.vlan.ip_addresses[0].broadcast > + end > + > + if bonding.ip_addresses.size > 0 > + # use the 1st assigned static ip > + bonding.ip_addr = bonding.ip_addresses[0].address > + end > + > + bonding.boot_type_id = bonding.vlan.boot_type_id > + > + bonding.save! > + end > + > + ############################################################## > + # drop ip_addresses table > + drop_table :ip_addresses > + > + # drop network ids from nics / bondings table > + remove_column :nics, :physical_network_id > + remove_column :bondings, :vlan_id > + > + # drop networks tables > + drop_table :networks_usages > + drop_table :usages > + drop_table :networks > + > + ############################################################## > + # undo bugfix above > + add_column :bondings_nics, :id, :integer > + end > +end > diff --git a/src/public/javascripts/ovirt.js b/src/public/javascripts/ovirt.js > index 4579c80..818b862 100644 > --- a/src/public/javascripts/ovirt.js > +++ b/src/public/javascripts/ovirt.js > @@ -297,4 +297,31 @@ function delete_pool(delete_url, id) > $.jGrowl(data.alert); > } > }, 'json'); > -} > \ No newline at end of file > +} > + > + > +function get_selected_networks() > +{ > + return get_selected_checkboxes("networks_grid_form"); > +} > + > +function afterNetwork(response, status){ > + ajax_validation(response, status); > + if (response.success) { > + $(document).trigger('close.facebox'); > + grid = $("#networks_grid"); > + if (grid.size()>0 && grid != null) { > + grid.flexReload(); > + } else { > + $tabs.tabs("load",$tabs.data('selected.tabs')); > + } > + } > +} > + > +function afterIpAddress(response, status){ > + ajax_validation(response, status); > + if (response.success) { > + reset_selected_ip_address(); > + reset_ip_address_select(); > + } > +} > diff --git a/src/public/stylesheets/components.css b/src/public/stylesheets/components.css > index 16eaf62..228ff7b 100644 > --- a/src/public/stylesheets/components.css > +++ b/src/public/stylesheets/components.css > @@ -267,4 +267,52 @@ > .detail-pane-chart { > height: 50px; > width: 375px; > -}; > +} > + > + > +/************************* > + * new popup components > + *************************/ > +.popup-content-selection { > + float: left; > + width: 45%; > + padding-left: 20px; > + padding-top: 10px; > +} > + > +.popup-content-selection select{ > + min-width: 200px; > +} > + > +.popup-content-footer{ > + width: 99%; > + float: left; > +} > + > +.selected_popup_content { > + padding-left: 20px; > + min-height: 50px; > + float: left; > + width: 96%; > +} > + > +#selected_popup_content_header { > + padding-bottom: 5px; > + font-weight: bold; > +} > + > +#selected_popup_content_expanded{ > + padding-bottom: 50px; > +} > + > +.selected_popup_content_left { > + float: left; > + width: 40%; > + padding-bottom: 15px; > +} > + > +.selected_popup_content_right { > + float: left; > + width: 40%; > + padding-bottom: 15px; > +}- -- Darryl L. Pierce <dpierce at redhat.com> : GPG KEYID: 6C4E7F1B -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iEYEARECAAYFAkkLEPoACgkQjaT4DmxOfxsw5wCaA5ztOXBYcMG/MEwiAJvm4xSh s7YAnRYUdFzlX5DNzjzePT/uq9Pdojw1 =geqD -----END PGP SIGNATURE----- -------------- next part -------------- A non-text attachment was scrubbed... Name: dpierce.vcf Type: text/x-vcard Size: 319 bytes Desc: not available URL: <http://listman.redhat.com/archives/ovirt-devel/attachments/20081031/ea295c8f/attachment.vcf>
Scott Seago
2008-Oct-31 14:59 UTC
[Ovirt-devel] [PATCH server] network integration into ovirt server db and wui
Mohammed Morsi wrote:> diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb > index a40d297..120fe2a 100644 > --- a/src/app/controllers/host_controller.rb > +++ b/src/app/controllers/host_controller.rb > @@ -156,6 +156,16 @@ class HostController < ApplicationController > render :json => @json_hash > end > > + def edit_network > + render :layout => 'popup' > + end > + > + def bondings_json > + bondings = Host.find(params[:id]).bondings > + bondings_json = [] > + bondings.each{ |x| bondings_json.push({:id => x.id, :name => x.name}) } > + render :json => bondings_json > + end >This is a really minor comment, but this could be written more compactly as: + def bondings_json + bondings = Host.find(params[:id]).bondings + render :json => bondings.collect{ |x| {:id => x.id, :name => x.name} + end> > > diff --git a/src/app/models/bonding.rb b/src/app/models/bonding.rb > index 006c261..a3ad150 100644 > --- a/src/app/models/bonding.rb > +++ b/src/app/models/bonding.rb > @@ -30,6 +30,16 @@ > # interface. They can be ignored if not used. > # > class Bonding < ActiveRecord::Base > + belongs_to :host > + belongs_to :bonding_type > + belongs_to :vlan > + has_many :ip_addresses, :dependent => :destroy > + > + has_and_belongs_to_many :nics, > + :join_table => 'bondings_nics', > + :foreign_key => :bonding_id > + > + > validates_presence_of :name, > :message => 'A name is required.' > > @@ -42,18 +52,17 @@ class Bonding < ActiveRecord::Base > validates_presence_of :interface_name, > :message => 'An interface name is required.' > > - validates_presence_of :boot_type_id, > - :message => 'A boot type must be specified.' > - > validates_presence_of :bonding_type_id, > :message => 'A bonding type must be specified.' > > - belongs_to :host > - belongs_to :bonding_type > - belongs_to :boot_type > + protected > + def validate > + errors.add("name", "must be specified") unless name > + errors.add("interface_name", "must be specified") unless interface_name > + errors.add("bonding_type_id", "must be specified") unless bonding_type_id > + errors.add("host_id", "must be specified") unless host_id > + errors.add("vlan_id", "must be specified") unless vlan_id > + end >Why not just use validates_presence_of for these? This seems like the standard out-of-the-box use case for validates_presence_of. Also, it looks like name and interface_name have the valiadition in both places -- validates_presence_of and validate.> > - has_and_belongs_to_many :nics, > - :join_table => 'bondings_nics', > - :foreign_key => :bonding_id > > end > diff --git a/src/app/models/physical_network.rb b/src/app/models/physical_network.rb > new file mode 100644 > index 0000000..cba696a > --- /dev/null > +++ b/src/app/models/physical_network.rb > @@ -0,0 +1,27 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +class PhysicalNetwork < Network > + has_many :nics > + > + protected > + def validate > + errors.add("name", "must be specified") unless name > + errors.add("boot_type_id", "must be specified") unless boot_type_id > + end > +end >Why not use validates_presence_of for these?> diff --git a/src/app/models/vlan.rb b/src/app/models/vlan.rb > new file mode 100644 > index 0000000..2b96502 > --- /dev/null > +++ b/src/app/models/vlan.rb > @@ -0,0 +1,30 @@ > +# Copyright (C) 2008 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +class Vlan < Network > + has_many :bondings > + > + validates_presence_of :number > + > + protected > + def validate > + errors.add("name", "must be specified") unless name > + errors.add("number", "must be specified") unless number > + errors.add("boot_type_id", "must be specified") unless boot_type_id > + end > +end >same comment on validates_presence_of here> diff --git a/src/app/views/dashboard/index.html.erb b/src/app/views/dashboard/index.html.erb > index 92cb4da..8815ebc 100644 > --- a/src/app/views/dashboard/index.html.erb > +++ b/src/app/views/dashboard/index.html.erb > @@ -13,6 +13,13 @@ > </table> > </div> > > + <% if @can_modify %> > + <h3>Networks</h3> > + <a href="<%= url_for :controller => 'network', :action => 'list' %>"> > + View / Edit > + </a> > + <% end %> > + > </div> <!-- end #tools --> > </td> >I'm not sure adding this to the currently-empty dashboard page is the right place -- although I realize the discussions on what to do with this were somewhat inconclusive. Here's another thought. Since it doesn't really belong to a "dashboard" per se -- and we're not sure that the dashboard needs multiple tabs, could we rename the nav column to something more generic (like "Home" or something). and then have Dashboard as ths first tab, Networks as another tab (if you have permissions on them), etc.? although this doesn't necessarily have to be done for this patch since it's more of a general UI cleanup task. The other issue here is that this breaks the left nav state since it's a standard http link instead of an ajax request. We should probably address that at the same time we place this on a tab in the UI> > diff --git a/src/app/views/host/edit_network.rhtml b/src/app/views/host/edit_network.rhtml > new file mode 100644 > index 0000000..7ec3180 > --- /dev/null > +++ b/src/app/views/host/edit_network.rhtml >For edit, we should remove the ability to change the type. Also the network details pane needs to show a list of associated IP addresses, Number (for vlans) and usages. And the 'new network' screen doesn't allow you to set usages either.> diff --git a/src/app/views/host/show.rhtml b/src/app/views/host/show.rhtml > index d1b05ad..bc39a62 100644 > --- a/src/app/views/host/show.rhtml > +++ b/src/app/views/host/show.rhtml > @@ -17,6 +17,10 @@ > <%= image_tag "icon_x.png" %> Clear VMs > </a> > <% end -%> > + <%= link_to image_tag("icon_edit.png") +"Edit Network", > + {:controller => 'host', > + :action => 'edit_network', :id => @host.id}, > + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> > <%- end -%> > <%- end -%> > <script type="text/javascript"> >The edit network link here lets me add new bonded interfaces and edit existing ones. The 'NICs' pulldown doesn't have an option to create a new one. Also the details pane doesn't show details on NICs or Bonded interfaces. Scott
Mohammed Morsi
2008-Oct-31 15:35 UTC
[Ovirt-devel] Re: [PATCH server] network integration into ovirt server db and wui
Based on Darryl's, Scott's, and Dan's feedback on my patch, the following things have been identified as needing to be done for the network component of the interface. I'm not sure all of this will be able to be done by the end of today (unless I can get some development help) so this list will need to be prioritized: 1. Remove 'validate' method, replace w/ 'validates_presence_of', 'validate_format_of' where we can 2. Change ManageNodeConfiguration bit, to use new network model / schema 3. Disable type selection for 'edit network' form 4. Associate vlan w/ physical_network, eg add parent_network_id to network table, allow this to be specified via new / edit network form (only for vlans) 5. Add ip address information to the network details pane 6. Add nic / bonding information to host details pane 7. VLANs should be associated with nics as well (seemingly small, but potentially large change) 8. Add usages multi-select box to create / edit network form 9. Remove 'add / edit networks' link from dashboard, use tabs 10. Add 'add / remove nics' functionality and interface 11. Small cleanup fixes>From what I see / heard, numbers 2,3, 8 are must-haves, eg my patchcan't be pushed w/ out these. The others, as important as they are, can wait for a subsequent bug fix patch. What do you all think, taking into consideration that we want this pushed by the end of today. -Mo
Mohammed Morsi
2008-Oct-31 21:14 UTC
[Ovirt-devel] [PATCH server] network integration into ovirt server db and wui
--- src/app/controllers/host_controller.rb | 14 +- src/app/controllers/network_controller.rb | 418 ++++++++++++++++++++ src/app/helpers/application_helper.rb | 28 +- src/app/helpers/network_helper.rb | 2 + src/app/models/bonding.rb | 30 +- src/app/models/ip_address.rb | 27 ++ src/app/models/ip_v4_address.rb | 53 +++ src/app/models/ip_v6_address.rb | 46 +++ src/app/models/network.rb | 32 ++ src/app/models/nic.rb | 21 +- src/app/models/physical_network.rb | 21 + src/app/models/usage.rb | 21 + src/app/models/vlan.rb | 24 ++ src/app/views/dashboard/index.html.erb | 7 + src/app/views/host/edit_network.rhtml | 85 ++++ src/app/views/host/show.rhtml | 8 + src/app/views/network/_bonding_form.rhtml | 46 +++ src/app/views/network/_form.rhtml | 46 +++ src/app/views/network/_grid.rhtml | 37 ++ src/app/views/network/_ip_address_form.rhtml | 62 +++ src/app/views/network/_ip_addresses_form.rhtml | 50 +++ src/app/views/network/_select.rhtml | 32 ++ src/app/views/network/edit.rhtml | 34 ++ src/app/views/network/edit_bonding.rhtml | 52 +++ src/app/views/network/edit_ip_address.rhtml | 76 ++++ .../views/network/edit_network_ip_addresses.rhtml | 13 + src/app/views/network/edit_nic.rhtml | 62 +++ src/app/views/network/list.html.erb | 72 ++++ src/app/views/network/new.rhtml | 28 ++ src/app/views/network/new_bonding.rhtml | 32 ++ src/app/views/network/new_ip_address.rhtml | 44 ++ src/app/views/network/show.rhtml | 31 ++ src/app/views/nic/_list.rhtml | 2 - src/db/migrate/028_refactor_networking_model.rb | 271 +++++++++++++ src/public/javascripts/ovirt.js | 21 +- src/public/stylesheets/components.css | 50 +++- 36 files changed, 1870 insertions(+), 28 deletions(-) create mode 100644 src/app/controllers/network_controller.rb create mode 100644 src/app/helpers/network_helper.rb create mode 100644 src/app/models/ip_address.rb create mode 100644 src/app/models/ip_v4_address.rb create mode 100644 src/app/models/ip_v6_address.rb create mode 100644 src/app/models/network.rb create mode 100644 src/app/models/physical_network.rb create mode 100644 src/app/models/usage.rb create mode 100644 src/app/models/vlan.rb create mode 100644 src/app/views/host/edit_network.rhtml create mode 100644 src/app/views/network/_bonding_form.rhtml create mode 100644 src/app/views/network/_form.rhtml create mode 100644 src/app/views/network/_grid.rhtml create mode 100644 src/app/views/network/_ip_address_form.rhtml create mode 100644 src/app/views/network/_ip_addresses_form.rhtml create mode 100644 src/app/views/network/_select.rhtml create mode 100644 src/app/views/network/edit.rhtml create mode 100644 src/app/views/network/edit_bonding.rhtml create mode 100644 src/app/views/network/edit_ip_address.rhtml create mode 100644 src/app/views/network/edit_network_ip_addresses.rhtml create mode 100644 src/app/views/network/edit_nic.rhtml create mode 100644 src/app/views/network/list.html.erb create mode 100644 src/app/views/network/new.rhtml create mode 100644 src/app/views/network/new_bonding.rhtml create mode 100644 src/app/views/network/new_ip_address.rhtml create mode 100644 src/app/views/network/show.rhtml create mode 100644 src/db/migrate/028_refactor_networking_model.rb diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb index a40d297..f961d1a 100644 --- a/src/app/controllers/host_controller.rb +++ b/src/app/controllers/host_controller.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -30,7 +30,7 @@ class HostController < ApplicationController end end - before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms] + before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms, :edit_network] # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => [:post, :put], :only => [ :create, :update ], @@ -81,7 +81,7 @@ class HostController < ApplicationController def addhost @hardware_pool = Pool.find(params[:hardware_pool_id]) - render :layout => 'popup' + render :layout => 'popup' end def add_to_smart_pool @@ -156,6 +156,14 @@ class HostController < ApplicationController render :json => @json_hash end + def edit_network + render :layout => 'popup' + end + + def bondings_json + bondings = Host.find(params[:id]).bondings + render :json => bondings.collect{ |x| {:id => x.id, :name => x.name} } + end private #filter methods diff --git a/src/app/controllers/network_controller.rb b/src/app/controllers/network_controller.rb new file mode 100644 index 0000000..56b7bd5 --- /dev/null +++ b/src/app/controllers/network_controller.rb @@ -0,0 +1,418 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. +# + +class NetworkController < ApplicationController + ########################## Networks related actions + + def network_permissions + # TODO more robust permission system + # either by subclassing network from pool + # or by extending permission model to accomodate + # any object + @default_pool = HardwarePool.get_default_pool + set_perms(@default_pool) + unless @can_modify + flash[:notice] = 'You do not have permission to view networks' + redirect_to :controller => 'dashboard' + end + end + + def list + @networks = Network.find(:all) + network_permissions + end + + def networks_json + json_list(Network.find(:all), [:id, :name, :type, [:boot_type, :label]]) + end + + def show + @network = Network.find(params[:id]) + network_permissions + respond_to do |format| + format.html { render :layout => 'selection' } + format.xml { render :xml => @network.to_xml } + end + end + + def new + @boot_types = BootType.find(:all) + @usage_types = Usage.find(:all) + render :layout => 'popup' + end + + def create + begin + @network = PhysicalNetwork.new(params[:network]) if params[:network][:type] == 'PhysicalNetwork' + @network = Vlan.new(params[:network]) if params[:network][:type] == 'Vlan' + @network.save! + alert = "Network was successfully created." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue + render :json => { :object => "network", :success => false, + :errors => + @network.errors.localize_error_messages.to_a } + end + end + + def edit + @network = Network.find(params[:id]) + @usage_types = Usage.find(:all) + @boot_types = BootType.find(:all) + render :layout => 'popup' + end + + def update + begin + @network = Network.find(params[:id]) + if @network.type == 'PhysicalNetwork' + @network = PhysicalNetwork.find(params[:id]) + else + @network = Vlan.find(params[:id]) + end + + @network.usages.delete_all + @network.update_attributes!(params[:network]) + + alert = "Network was successfully updated." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue Exception => e + render :json => { :object => "network", :success => false, + :errors => + @network.errors.localize_error_messages.to_a } + end + end + + def delete + failed_networks = [] + networks_ids_str = params[:network_ids] + network_ids = networks_ids_str.split(",").collect {|x| x.to_i} + network_ids.each{ |x| + network = Network.find(x) + unless network.type.to_s == 'Vlan' && + Vlan.find(x).bondings.size != 0 || + network.type.to_s == 'PhysicalNetwork' && + PhysicalNetwork.find(x).nics.size != 0 + begin + Network.destroy(x) + rescue + failed_networks.push x + end + else + failed_networks.push x + end + } + if failed_networks.size == 0 + render :json => { :object => "network", + :success => true, + :alert => "Successfully deleted networks" } + else + render :json => { :object => "network", + :success => false, + :alert => "Failed deleting " + + failed_networks.size.to_s + + " networks due to existing bondings/nics" } + end + end + + def edit_network_ip_addresses + @network = Network.find(params[:id]) + render :layout => 'popup' + end + + + ########################## Ip Address related actions + + def ip_addresses_json + @parent_type = params[:parent_type] + if @parent_type == 'network' + ip_addresses = Network.find(params[:id]).ip_addresses + elsif @parent_type == 'nic' + ip_addresses = Nic.find(params[:id]).ip_addresses + elsif @parent_type == 'bonding' and params[:id] + ip_addresses = Bonding.find(params[:id]).ip_addresses + else + ip_addresses = [] + end + + ip_addresses_json = [] + ip_addresses.each{ |x| + ip_addresses_json.push({:id => x.id, :name => x.address}) } + render :json => ip_addresses_json + end + + def new_ip_address + @parent_type = params[:parent_type] + @network = Network.find(params[:id]) if @parent_type == 'network' + @nic = Nic.find(params[:id]) if @parent_type == 'nic' + @bonding = Bonding.find(params[:id]) if @parent_type == 'bonding' and params[:id] + + render :layout => false + end + + def _create_ip_address + if params[:ip_address][:type] == "IpV4Address" + @ip_address = IpV4Address.new(params[:ip_address]) + else + @ip_address = IpV6Address.new(params[:ip_address]) + end + @ip_address.save! + end + + def create_ip_address + begin + _create_ip_address + alert = "Ip Address was successfully created." + render :json => { :object => "ip_address", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a } + end + end + + def edit_ip_address + @ip_address = IpAddress.find(params[:id]) + + @parent_type = params[:parent_type] + @network = @ip_address.network if @ip_address.network_id + @nic = @ip_address.nic if @ip_address.nic_id + @bonding = @ip_address.bonding if @ip_address.bonding_id + + render :layout => false + end + + def _update_ip_address(id) + @ip_address = IpAddress.find(id) + + # special case if we are switching types + if @ip_address.type != params[:ip_address][:type] + if params[:ip_address][:type] == 'IpV4Address' + @ip_address = IpV4Address.new(params[:ip_address]) + @ip_address.save! + IpV6Address.delete(id) + else + @ip_address = IpV6Address.new(params[:ip_address]) + @ip_address.save! + IpV4Address.delete(id) + end + else + if @ip_address.type == 'IpV4Address' + @ip_address = IpV4Address.find(id) + else + @ip_address = IpV6Address.find(id) + end + @ip_address.update_attributes!(params[:ip_address]) + end + + end + + def update_ip_address + begin + _update_ip_address(params[:id]) + alert = "IpAddress was successfully updated." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a } + end + end + + def destroy_ip_address + begin + IpAddress.delete(params[:id]) + alert = "Ip Address was successfully deleted." + render :json => { :object => "ip_address", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :alert => 'Ip Address Deletion Failed' } + end + end + + + ########################## NICs related actions + + def edit_nic + @nic = Nic.find(params[:id]) + @network = @nic.physical_network + + @networks = PhysicalNetwork.find(:all) + network_options + + render :layout => false + end + + def update_nic + begin + network_options + @network = Network.find(params[:nic][:physical_network_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @nic = Nic.find(params[:id]) + @nic.update_attributes!(params[:nic]) + + alert = "Nic was successfully updated." + render :json => { :object => "nic", :success => true, + :alert => alert } + rescue Exception => e + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "nic", :success => false, + :errors => + @nic.errors.localize_error_messages.to_a } + end + end + end + + ########################## Bonding related actions + + def new_bonding + unless params[:host_id] + flash[:notice] = "Host is required." + redirect_to :controller => 'dashboard' + end + + @host = Host.find(params[:host_id]) + @networks = Vlan.find(:all) + network_options + + render :layout => false + end + + def create_bonding + begin + network_options + @network = Network.find(params[:bonding][:vlan_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @bonding = Bonding.new(params[:bonding]) + @bonding.save! + + if @ip_address + @ip_address.bonding_id = @bonding.id + @ip_address.save! + end + + alert = "Bonding was successfully created." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "bonding", :success => false, + :errors => + @bonding.errors.localize_error_messages.to_a } + end + end + end + + def edit_bonding + @bonding = Bonding.find(params[:id]) + @network = @bonding.vlan + + @host = @bonding.host + @networks = Vlan.find(:all) + network_options + + render :layout => false + end + + def update_bonding + begin + network_options + @network = Network.find(params[:bonding][:vlan_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @bonding = Bonding.find(params[:id]) + @bonding.nics.delete_all + @bonding.update_attributes!(params[:bonding]) + + alert = "Bonding was successfully updated." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "bonding", :success => false, + :errors => + @bonding.errors.localize_error_messages.to_a } + end + end + end + + def destroy_bonding + begin + Bonding.destroy(params[:id]) + alert = "Bonding was successfully deleted." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + render :json => { :object => "bonding", :success => false, + :alert => 'Bonding Deletion Failed' } + end + + end + + + ########################## Misc methods + + protected + def network_options + @bonding_types = BondingType.find(:all) + @static_boot_type = BootType.find(:first, + :conditions => { :proto => 'static' }) + end + +end diff --git a/src/app/helpers/application_helper.rb b/src/app/helpers/application_helper.rb index d7b6628..0178ad0 100644 --- a/src/app/helpers/application_helper.rb +++ b/src/app/helpers/application_helper.rb @@ -84,21 +84,31 @@ module ApplicationHelper } end - def popup_footer(action, label) - %{ - <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0;"> + # expects hash of labels => actions + def multi_button_popup_footer(buttons = {}) + buttons_html = "" + buttons.each{ |label, action| + buttons_html+="<div class=\"button\">" + + " <div class=\"button_left_blue\"></div>" + + " <div class=\"button_middle_blue\"><a href=\"#\" onclick=\"#{action}\">#{label}</a></div>" + + " <div class=\"button_right_blue\"></div>" + + "</div>" + } + + %{ + <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0; float: left; width: 97%;"> <div class="button"> <div class="button_left_grey"></div> <div class="button_middle_grey"><a href="#" onclick="$(document).trigger('close.facebox')">Cancel</a></div> <div class="button_right_grey"></div> </div> - <div class="button"> - <div class="button_left_blue"></div> - <div class="button_middle_blue"><a href="#" onclick="#{action}">#{label}</a></div> - <div class="button_right_blue"></div> - </div> + #{buttons_html} </div> - } + } + end + + def popup_footer(action, label) + multi_button_popup_footer({label => action}) end def ok_footer diff --git a/src/app/helpers/network_helper.rb b/src/app/helpers/network_helper.rb new file mode 100644 index 0000000..ebbce0b --- /dev/null +++ b/src/app/helpers/network_helper.rb @@ -0,0 +1,2 @@ +module NetworkHelper +end diff --git a/src/app/models/bonding.rb b/src/app/models/bonding.rb index 006c261..c9af38c 100644 --- a/src/app/models/bonding.rb +++ b/src/app/models/bonding.rb @@ -30,6 +30,16 @@ # interface. They can be ignored if not used. # class Bonding < ActiveRecord::Base + belongs_to :host + belongs_to :bonding_type + belongs_to :vlan + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :nics, + :join_table => 'bondings_nics', + :foreign_key => :bonding_id + + validates_presence_of :name, :message => 'A name is required.' @@ -42,18 +52,20 @@ class Bonding < ActiveRecord::Base validates_presence_of :interface_name, :message => 'An interface name is required.' - validates_presence_of :boot_type_id, - :message => 'A boot type must be specified.' - validates_presence_of :bonding_type_id, :message => 'A bonding type must be specified.' - belongs_to :host - belongs_to :bonding_type - belongs_to :boot_type + validates_presence_of :vlan_id, + :message => 'A vlan must be specified.' + + protected + def validate + if vlan.boot_type.proto == 'static' and ip_addresses.size == 0 + errors.add("vlan_id", + "is static. Must create at least one static ip") + end + + end - has_and_belongs_to_many :nics, - :join_table => 'bondings_nics', - :foreign_key => :bonding_id end diff --git a/src/app/models/ip_address.rb b/src/app/models/ip_address.rb new file mode 100644 index 0000000..5d2e6af --- /dev/null +++ b/src/app/models/ip_address.rb @@ -0,0 +1,27 @@ +# ip_address.rb +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpAddress+ is the base class for all address related classes. +# +class IpAddress < ActiveRecord::Base + # one of these 3 will apply for each address + belongs_to :network + belongs_to :nic + belongs_to :bonding +end diff --git a/src/app/models/ip_v4_address.rb b/src/app/models/ip_v4_address.rb new file mode 100644 index 0000000..f3128a4 --- /dev/null +++ b/src/app/models/ip_v4_address.rb @@ -0,0 +1,53 @@ +# ip_v4_address.rb +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com>, +# Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpV4Address+ represents a single IPv4 address. +# +class IpV4Address < IpAddress + ADDRESS_TEST = %r{^(\d{1,3}\.){3}\d{1,3}$} + + validates_presence_of :address, + :message => 'An address must be supplied.' + validates_format_of :address, + :with => ADDRESS_TEST + + validates_presence_of :netmask, + :message => 'A netmask must be supplied.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :netmask, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + + validates_presence_of :gateway, + :message => 'A gateway address must be supplied.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :gateway, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + + validates_presence_of :broadcast, + :message => 'A broadcast address must be supplied.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :broadcast, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + +end diff --git a/src/app/models/ip_v6_address.rb b/src/app/models/ip_v6_address.rb new file mode 100644 index 0000000..11951cc --- /dev/null +++ b/src/app/models/ip_v6_address.rb @@ -0,0 +1,46 @@ +# ip_v6_address.rb +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com>, +# Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpV6Address+ represents a single IPv6 address. +# +class IpV6Address < IpAddress + ADDRESS_TEST = %r{^([0-9a-fA-F]{0,4}|0)(\:([0-9a-fA-F]{0,4}|0)){7}$} + + validates_presence_of :address, + :message => 'An address must be provided.' + validates_format_of :address, + :with => ADDRESS_TEST + + validates_presence_of :gateway, + :message => 'A gateway address must be provided.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :gateway, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + + validates_presence_of :prefix, + :message => 'A prefix must be provided.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :prefix, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + +end diff --git a/src/app/models/network.rb b/src/app/models/network.rb new file mode 100644 index 0000000..404633d --- /dev/null +++ b/src/app/models/network.rb @@ -0,0 +1,32 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Network < ActiveRecord::Base + belongs_to :boot_type + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :usages, :join_table => 'networks_usages' + + validates_presence_of :type, + :message => 'A type must be specified.' + validates_presence_of :name, + :message => 'A name must be specified.' + validates_presence_of :boot_type_id, + :message => 'A boot type must be specified.' + +end diff --git a/src/app/models/nic.rb b/src/app/models/nic.rb index baf7095..5649763 100644 --- a/src/app/models/nic.rb +++ b/src/app/models/nic.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -19,7 +19,22 @@ class Nic < ActiveRecord::Base belongs_to :host - belongs_to :boot_type + belongs_to :physical_network + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :bondings, :join_table => 'bondings_nics' + + validates_presence_of :host_id, + :message => 'A host must be specified.' + + validates_presence_of :physical_network_id, + :message => 'A network must be specified.' - has_and_belongs_to_many :bonding, :join_table => 'bondings_nics' + protected + def validate + if physical_network.boot_type.proto == 'static' and ip_addresses.size == 0 + errors.add("physical_network_id", + "is static. Must create at least one static ip") + end + end end diff --git a/src/app/models/physical_network.rb b/src/app/models/physical_network.rb new file mode 100644 index 0000000..6923e40 --- /dev/null +++ b/src/app/models/physical_network.rb @@ -0,0 +1,21 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class PhysicalNetwork < Network + has_many :nics +end diff --git a/src/app/models/usage.rb b/src/app/models/usage.rb new file mode 100644 index 0000000..353e8f4 --- /dev/null +++ b/src/app/models/usage.rb @@ -0,0 +1,21 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Usage < ActiveRecord::Base + has_and_belongs_to_many :networks, :join_table => 'networks_usages' +end diff --git a/src/app/models/vlan.rb b/src/app/models/vlan.rb new file mode 100644 index 0000000..f7889f4 --- /dev/null +++ b/src/app/models/vlan.rb @@ -0,0 +1,24 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Vlan < Network + has_many :bondings + + validates_presence_of :number, + :message => 'A number must be specified.' +end diff --git a/src/app/views/dashboard/index.html.erb b/src/app/views/dashboard/index.html.erb index 92cb4da..8815ebc 100644 --- a/src/app/views/dashboard/index.html.erb +++ b/src/app/views/dashboard/index.html.erb @@ -13,6 +13,13 @@ </table> </div> + <% if @can_modify %> + <h3>Networks</h3> + <a href="<%= url_for :controller => 'network', :action => 'list' %>"> + View / Edit + </a> + <% end %> + </div> <!-- end #tools --> </td> diff --git a/src/app/views/host/edit_network.rhtml b/src/app/views/host/edit_network.rhtml new file mode 100644 index 0000000..7ec3180 --- /dev/null +++ b/src/app/views/host/edit_network.rhtml @@ -0,0 +1,85 @@ +<%- content_for :title do -%> + Edit <%= @host.hostname %> Network Devices +<%- end -%> + +<%- content_for :description do -%> + Select and edit nics and bonded interfaces on <%= @host.hostname %> +<%- end -%> + +<div id="select-host-nic" class="popup-content-selection"> +<%= select_with_label "NICs", "nic", "id", + @host.nics. + collect{ |nic| [nic.mac, nic.id] }. + insert(0, "") %> +</div> + +<div id="select-host-bonding" class="popup-content-selection"> +<%= select_with_label "Bonded Interfaces", "bonding", "id", [] %> +</div> + +<div style="clear: both;"></div> + +<div id="selected_nic_bonding" class="selected_popup_content"></div> + +<div id="host_network_footer" class="popup-content-footer"> + <%= ok_footer %> +</div> + +<script type="text/javascript"> +function reset_nics_bonding_detail(){ + var data='Select NIC or Bonded Interface<br/>'; + + $("#selected_nic_bonding").html(data); + $("#host_network_footer").show(); +}; + +reset_nics_bonding_detail(); // run it once for inital content + +function reset_nics_select(){ + $("#nic_id option:first").attr("selected", true); +}; + +function reset_bonding_select(){ + // incase of new additions / deletions, repopulate select box + $.getJSON( + "<%= url_for :action => 'bondings_json', :id => @host.id %>", + {}, + function(j){ + var options = "<option value=''></option>" + + "<option value='New'>New</option>"; + for(var i = 0; i < j.length; i++){ + options += '<option value="' + j[i].id + '">' + j[i].name + + '</option>'; + } + $("#bonding_id").html(options); + }); + + $("#bonding_id option:first").attr("selected", true); +}; + +reset_bonding_select(); // run it once for initial content + +$("#nic_id").change(function () { + reset_bonding_select(); + if($('#nic_id').val() != ""){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', + :action => 'edit_nic'%>/" + $('#nic_id').val()); + $("#host_network_footer").hide(); + }else{ + reset_nics_bonding_detail(); + } +}); + +$("#bonding_id").change(function () { + reset_nics_select(); + if($('#bonding_id').val() == "New"){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'new_bonding', :host_id => @host.id %>"); + $("#host_network_footer").hide(); + }else if($('#bonding_id').val() != ""){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'edit_bonding'%>/" + $('#bonding_id').val()); + $("#host_network_footer").hide(); + }else{ + reset_nics_bonding_detail(); + } +}); +</script> diff --git a/src/app/views/host/show.rhtml b/src/app/views/host/show.rhtml index d1b05ad..c9562b1 100644 --- a/src/app/views/host/show.rhtml +++ b/src/app/views/host/show.rhtml @@ -17,6 +17,10 @@ <%= image_tag "icon_x.png" %> Clear VMs </a> <% end -%> + <%= link_to image_tag("icon_edit.png") +"Edit Network", + {:controller => 'host', + :action => 'edit_network', :id => @host.id}, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> <%- end -%> <%- end -%> <script type="text/javascript"> @@ -47,6 +51,8 @@ Hypervisor Type:<br/> Status:<br/> VMs:<br/> + NICs:<br/> + Bondings:<br/> </div> <div class="selection_value"> <%=h @host.uuid %><br/> @@ -56,6 +62,8 @@ <%=h @host.arch %><br/> <%=h @host.hypervisor_type %><br/> <%=h @host.status_str %><br/> + <%= @host.nics.collect{ |n| n.mac }.join("<br/>") %><br/> + <%=h @host.bondings.collect { |n| n.name }.join("<br/>") %><br/> <%= @host.vms.collect{|x| x.uuid }.join(" <br/> ") %><br/> <!-- FIXME: need styling diff --git a/src/app/views/network/_bonding_form.rhtml b/src/app/views/network/_bonding_form.rhtml new file mode 100644 index 0000000..4ec0df1 --- /dev/null +++ b/src/app/views/network/_bonding_form.rhtml @@ -0,0 +1,46 @@ + <%= error_messages_for 'bonding' %> + <%= error_messages_for 'ip_address' %> + +<div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'bonding[host_id]', @host.id %> + + <div class="selected_popup_content_left">Name</div> + <div class="selected_popup_content_right"> + <%= text_field_with_label "", "bonding", "name" %> + </div> + + <div class="selected_popup_content_left">Interface Name</div> + <div class="selected_popup_content_right"> + <%= text_field_with_label "", "bonding", "interface_name" %> + </div> + + <div class="selected_popup_content_left">Bonding Type</div> + <div class="selected_popup_content_right"> + <%= select "bonding", "bonding_type_id", + @bonding_types.collect { |bt| [bt.label, bt.id ] } %> + </div> + + <% if @host.nics.size != 0 %> + <div class="selected_popup_content_left">NICs</div> + <div class="selected_popup_content_right"> + <select id="bonding_nic_ids" name="bonding[nic_ids][]" multiple="true"> + <%= options_from_collection_for_select @host.nics, "id", "mac", + @bonding ? @bonding.nics.collect{ |x| x.nic_id.to_i } : [] %> + </select> + </div> + <% end %> + + <%= render :partial => 'select', :locals => { :target => 'bonding' } %> + +<div id="static_ip_options" + style="<% unless ((@network && @network.boot_type_id == @static_boot_type.id) || + (!@network && @networks.size > 0 && + @networks[0].boot_type_id == @static_boot_type.id)) %> + display: none;<%end %>"> + <%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'bonding', + :parent_id => @bonding ? @bonding.id : nil } %> +</div> + + +</div> diff --git a/src/app/views/network/_form.rhtml b/src/app/views/network/_form.rhtml new file mode 100644 index 0000000..6c67a0e --- /dev/null +++ b/src/app/views/network/_form.rhtml @@ -0,0 +1,46 @@ +<%= error_messages_for 'network' %> + +<!--[form:network]--> +<%= hidden_field 'network', 'id' if @create %> + +<%= text_field_with_label "Name:", "network", "name", + {:style=>"width:250px;"} %> + +<%= select_with_label "Boot Type:", 'network', 'boot_type_id', + @boot_types.collect{ |bt| [bt.label, bt.id] }, + :style=>"width:250px;" %> + +<% if create %> +<%= select_with_label "Type", "network", "type", + [[ "Physical Network", "PhysicalNetwork" ], + [ "VLAN", "Vlan" ] ] %> +<% end %> + + +<div class="field_title">Usage:</div> +<div class="form_field"> + <select id="network_usages_ids" name="network[usage_ids][]" multiple="true"> + <%= options_from_collection_for_select @usage_types, "id", "label", + @network ? @network.usages.collect{ |x| x.id.to_i } : [] %> + </select> +</div> + +<div id="vlan_options" + <% if @network && @network.type != 'Vlan' %> + style="display: none;" + <% end %> +> +<%= text_field_with_label "Number:", "network", "number", + {:style=>"width:250px;"} %> +</div> + + +<script type="text/javascript"> +$("#network_type").change(function () { + if($('#network_type').val() == "Vlan"){ + $("#vlan_options").show(); + }else{ + $("#vlan_options").hide(); + } +}).trigger('change'); +</script> diff --git a/src/app/views/network/_grid.rhtml b/src/app/views/network/_grid.rhtml new file mode 100644 index 0000000..6af0c05 --- /dev/null +++ b/src/app/views/network/_grid.rhtml @@ -0,0 +1,37 @@ +<% networks_per_page = 40 unless (defined? networks_per_page) and !(networks_per_page.nil?) %> +<% usepager = @networks.size > networks_per_page %> + +<div id="<%= table_id %>_div"> +<form id="<%= table_id %>_form"> +<table id="<%= table_id %>" style="display:none"></table> +</form> +</div> +<script type="text/javascript"> + $("#<%= table_id %>").flexigrid + ( + { + url: '<%= url_for :action => "networks_json" %>', + dataType: 'json', + colModel : [ + {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, + {display: 'Name', name: 'name', width : 180, align: 'left'}, + {display: 'Type', name: 'type', width : 180, align: 'left'}, + {display: 'Boot Type', name: 'boot_type', width : 180, align: 'left'} + ], + sortname: "name", + sortorder: "asc", + usepager: <%= usepager %>, + useRp: <%= usepager %>, + rp: <%= networks_per_page %>, + showTableToggleBtn: true, + onSelect: <%= on_select %> + } + ); + function <%= table_id %>checkbox(celDiv) + { + $(celDiv).html('<input type="checkbox" name="grid_checkbox'+$(celDiv).html()+'" class="grid_checkbox" value="'+$(celDiv).html()+'"/>'); + } + +</script> + + diff --git a/src/app/views/network/_ip_address_form.rhtml b/src/app/views/network/_ip_address_form.rhtml new file mode 100644 index 0000000..b952807 --- /dev/null +++ b/src/app/views/network/_ip_address_form.rhtml @@ -0,0 +1,62 @@ +<%= error_messages_for 'ip_address' %> + +<div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'parent_type', @parent_type%> + <%= hidden_field_tag 'ip_address[id]', @ip_address.id if @ip_address%> + <%= hidden_field_tag 'ip_address[network_id]', @network.id if @network %> + <%= hidden_field_tag 'ip_address[nic_id]', @nic.id if @nic %> + <%= hidden_field_tag 'ip_address[bonding_id]', @bonding.id if @bonding %> + + <div class="selected_nic_bonding_left">Type:</div> + <div class="selected_nic_bonding_right"> + <%= select_with_label "", "ip_address", "type", + [[ "ipv4", "IpV4Address" ], [ "ipv6", "IpV6Address" ] ] %> + </div> + + + <div class="static_ip_common_options"> + <div class="selected_nic_bonding_left">IP Address</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "address" %> + </div> + </div> + + <div id="static_ip_v4_options"> + <div class="selected_nic_bonding_left">Netmask</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "netmask" %> + </div> + + <div class="selected_nic_bonding_left">Broadcast</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "broadcast" %> + </div> + </div> + + <div id="static_ip_v6_options" style="display: none;"> + <div class="selected_nic_bonding_left">Prefix</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "prefix" %> + </div> + </div> + + <div class="static_ip_common_options"> + <div class="selected_nic_bonding_left">Gateway</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "gateway" %> + </div> + </div> + +</div> + +<script type="text/javascript"> + $("#ip_address_type").change(function () { + if($("#ip_address_type").val() == 'IpV4Address'){ + $("#static_ip_v4_options").show(); + $("#static_ip_v6_options").hide(); + }else{ + $("#static_ip_v4_options").hide(); + $("#static_ip_v6_options").show(); + } + }).trigger('change'); +</script> diff --git a/src/app/views/network/_ip_addresses_form.rhtml b/src/app/views/network/_ip_addresses_form.rhtml new file mode 100644 index 0000000..f833b2a --- /dev/null +++ b/src/app/views/network/_ip_addresses_form.rhtml @@ -0,0 +1,50 @@ +<div id="select_ip_address" class="popup-content-selection"> +<%= select_with_label "IP Addresses", "ip_address", "id", [] %> +</div> + +<div id="selected_ip_address" class="selected_popup_content"></div> + +<script type="text/javascript"> +function reset_selected_ip_address(){ + var data='Select IP Address<br/>'; + + $("#selected_ip_address").html(data); + $("#ip_addresses_footer").show(); +}; + +reset_selected_ip_address(); // run it once for inital content + +function reset_ip_address_select(){ + // incase of new additions / deletions, repopulate select box + $.getJSON( + "<%= url_for :action => 'ip_addresses_json', :id => parent_id, :parent_type => parent_type %>", + {}, + function(j){ + var options = "<option value=''></option>" + + "<option value='New'>New</option>"; + for(var i = 0; i < j.length; i++){ + options += '<option value="' + j[i].id + '">' + j[i].name + + '</option>'; + } + $("#ip_address_id").html(options); + }); + + $("#ip_address_id option:first").attr("selected", true); +}; + +reset_ip_address_select(); // run it once for initial content + +$("#ip_address_id").change(function () { + if($('#ip_address_id').val() == "New"){ + $("#selected_ip_address").load("<%= url_for :action => 'new_ip_address', :id => parent_id, :parent_type => parent_type %>"); + $("#ip_addresses_footer").hide(); + }else if($('#ip_address_id').val() != ""){ + $("#selected_ip_address").load("<%= url_for :action => 'edit_ip_address'%>/" + $('#ip_address_id').val() + "?parent_type=<%= parent_type %>"); + $("#ip_addresses_footer").hide(); + }else{ + reset_selected_ip_address(); + } +}); + +</script> + diff --git a/src/app/views/network/_select.rhtml b/src/app/views/network/_select.rhtml new file mode 100644 index 0000000..e69d9b0 --- /dev/null +++ b/src/app/views/network/_select.rhtml @@ -0,0 +1,32 @@ +<% target = 'nic' unless target + network_id = 'physical_network_id' if target == 'nic' + network_id = 'vlan_id' if target == 'bonding' + + %> + +<div class="selected_popup_content_left">Network:</div> +<div class="selected_popup_content_right"> + <%= select_with_label "", target, network_id, + @networks.collect { |n| [n.name + ' - ' + n.boot_type.label, n.id ] } %> + +</div> + +<script type="text/javascript"> + var static_network_ids=<%= static_network_ids = '[' + @networks.each { |n| + static_network_ids += ',' if static_network_ids != '[' + static_network_ids += n.id.to_s if n.boot_type_id == @static_boot_type.id + } + static_network_ids += ']' + static_network_ids %>; + + $("#<%=target %>_<%= network_id %>").change(function () { + $("#static_ip_options").hide(); + for(i=0; i<static_network_ids.length; ++i){ + if($("#<%= target %>_<%= network_id %>").val() == static_network_ids[i]){ + $("#static_ip_options").show(); + break; + } + }; + }); +</script> diff --git a/src/app/views/network/edit.rhtml b/src/app/views/network/edit.rhtml new file mode 100644 index 0000000..6360b3f --- /dev/null +++ b/src/app/views/network/edit.rhtml @@ -0,0 +1,34 @@ +<%- content_for :title do -%> + Edit Network +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + + <!-- DIALOG BODY --> + <form method="POST" action="<%= url_for :action => 'update' %>" id="network_form" > + <div class="dialog_form"> + <%= hidden_field_tag 'id', @network.id %> + <%= render :partial => 'form', :locals => { :create => false } %> + </div> + <!-- DIALOG FOOTER --> + <%= popup_footer("$('#network_form').submit()", "Edit Network") %> + </form> + +<script type="text/javascript"> +$(function() { + var networkoptions = { + target: '<%= url_for :action => 'update' %>', + dataType: 'json', + success: function(response, status) { + afterNetwork(response, status); + refresh_summary_static('networks_selection', + '<div class="selection_left"><div>Select a network.</div></div>'); + } + }; + + // bind form using 'ajaxForm' + $('#network_form').ajaxForm(networkoptions); +}); +</script> + + diff --git a/src/app/views/network/edit_bonding.rhtml b/src/app/views/network/edit_bonding.rhtml new file mode 100644 index 0000000..645e469 --- /dev/null +++ b/src/app/views/network/edit_bonding.rhtml @@ -0,0 +1,52 @@ +<form method="POST" + action="<%= url_for :action => 'update_bonding' %>" id="edit_bonding_form" > + <div id="selected_popup_content_header"> + Editing Bonded Interface + </div> + + <%= hidden_field_tag('id', @bonding.id) %> + <%= render :partial => 'bonding_form' %> + +</form> + +<form method="POST" action="<%= url_for :action => 'destroy_bonding' %>" id="delete_bonding_form" > + <%= hidden_field_tag('id', @bonding.id) %> +</form> + +<%= multi_button_popup_footer({"Edit Bonding" => + "$('#edit_bonding_form').submit()", + "Delete Bonding" => + "$('#delete_bonding_form').submit()"}) %> + +<script type="text/javascript"> +$(function() { + var edit_bonding_options = { + target: '<%= url_for :action => 'update_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + } + } + }; + + var delete_bonding_options = { + target: '<%= url_for :action => 'destroy_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind forms using 'ajaxForm' + $('#edit_bonding_form').ajaxForm(edit_bonding_options); + $('#delete_bonding_form').ajaxForm(delete_bonding_options); + +}); +</script> diff --git a/src/app/views/network/edit_ip_address.rhtml b/src/app/views/network/edit_ip_address.rhtml new file mode 100644 index 0000000..60fe8f1 --- /dev/null +++ b/src/app/views/network/edit_ip_address.rhtml @@ -0,0 +1,76 @@ +<% if @parent_type == 'network' %> +<form method="POST" + action="<%= url_for :action => 'update_ip_address' %>" + id="edit_ip_address_form" > +<% end %> + + <div id="selected_popup_content_header"> + Editing IP Address + </div> + + <% if @parent_type != 'network' %> + <a href="#" onClick="$('#delete_ip_address_form').submit()" style="color: blue;">Delete</a> + <% end %> + + <%= hidden_field_tag('id', @ip_address.id) %> + <%= render :partial => 'ip_address_form' %> + +<% if @parent_type == 'network' %> +</form> +<% end %> + +<form method="POST" + action="<%= url_for :action => 'destroy_ip_address' %>" + id="delete_ip_address_form" > + <%= hidden_field_tag('id', @ip_address.id) %> +</form> + +<% if @parent_type == 'network' %> +<%= multi_button_popup_footer({" Edit IP Address" => + "$('#edit_ip_address_form').submit()", + "Delete IP Address" => + "$('#delete_ip_address_form').submit()"}) %> +<% end %> + +<script type="text/javascript"> +$(function() { + var edit_ip_address_options = { + target: '<%= url_for :action => 'update_ip_address' %>', // target element to update + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + <% if @parent_type == 'network' %> + refresh_summary('networks_selection', + '<%= url_for :controller => "network", :action => "show" %>', + <%= @network.id %>); + <% end %> + } + } + }; + + var delete_ip_address_options = { + target: '<%= url_for :action => 'delete_ip_address' %>', // target element to update + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + <% if @parent_type == 'network' %> + refresh_summary('networks_selection', + '<%= url_for :controller => "network", :action => "show" %>', + <%= @network.id %>); + <% end %> + } + } + }; + + // bind forms using 'ajaxForm' + $('#edit_ip_address_form').ajaxForm(edit_ip_address_options); + $('#delete_ip_address_form').ajaxForm(delete_ip_address_options); + +}); +</script> diff --git a/src/app/views/network/edit_network_ip_addresses.rhtml b/src/app/views/network/edit_network_ip_addresses.rhtml new file mode 100644 index 0000000..7a1e4cb --- /dev/null +++ b/src/app/views/network/edit_network_ip_addresses.rhtml @@ -0,0 +1,13 @@ +<%- content_for :title do -%> + Edit Network IP Addresses +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + +<%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'network', + :parent_id => @network.id } %> + +<div id="ip_addresses_footer" class="popup-content-footer"> + <%= ok_footer %> +</div> diff --git a/src/app/views/network/edit_nic.rhtml b/src/app/views/network/edit_nic.rhtml new file mode 100644 index 0000000..75af6fb --- /dev/null +++ b/src/app/views/network/edit_nic.rhtml @@ -0,0 +1,62 @@ +<form method="POST" + action="<%= url_for :action => 'update_nic' %>" id="nic_form" > + <div id="selected_popup_content_header"> + Editing NIC + </div> + + <%= error_messages_for 'nic' %> + + <div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'id', @nic.id %> + <%= hidden_field_tag 'nic_host_id', @nic.host.id %> + <%= hidden_field_tag 'nic_network_id', @nic.physical_network.id %> + + <div class="selected_popup_content_left">MAC:</div> + <div class="selected_popup_content_right"><%= @nic.mac %></div> + + <% if @nic.host.bondings.size != 0 %> + <div class="selected_popup_content_left">Bonded Interfaces</div> + <div class="selected_popup_content_right"> + <select id="nic_bonding_ids" name="nic[bonding_ids][]"> + <option value="" /> + <%= options_from_collection_for_select @nic.host.bondings, + "id", "name", + @nic.bondings.size > 0 ? @nic.bondings[0].bonding_id.to_i : "" %> + </select> + </div> + <% end %> + + + <%= render :partial => 'select' %> + + <div id="static_ip_options" + style="<% if @network.boot_type_id != @static_boot_type.id %> + display: none;<%end %>"> + <%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'nic', + :parent_id => @nic.id } %> + </div> + + + </div> + <%= popup_footer("$('#nic_form').submit()", "Edit NIC") %> +</form> + +<script type="text/javascript"> +$(function() { + var nicoptions = { + target: '<%= url_for :action => 'update_nic' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_nics_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind form using 'ajaxForm' + $('#nic_form').ajaxForm(nicoptions); +}); +</script> diff --git a/src/app/views/network/list.html.erb b/src/app/views/network/list.html.erb new file mode 100644 index 0000000..9a304cf --- /dev/null +++ b/src/app/views/network/list.html.erb @@ -0,0 +1,72 @@ +<div id="toolbar_nav"> +<ul> + <li><a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]"><%= image_tag "icon_addstorage.png", :style => "vertical-align:middle;" %> Add Network</a></li> + <li> + <a href="#" onClick="delete_networks();" > + <%= image_tag "icon_remove.png", :style=>"vertical-align:middle;" %> + Remove + </a> + </li> + </ul> +</div> + +<script type="text/javascript"> + function delete_networks(){ + var networks = get_selected_networks(); + if (validate_selected(get_selected_networks(), 'networks')) { + $.post('<%= url_for :action => 'delete' %>', + { network_ids: networks.toString() }, + function(data,status){ + if (data.alert) { + $.jGrowl(data.alert); + } + grid = $("#networks_grid"); + if (grid.size()>0 && grid != null) { + grid.flexReload(); + } + empty_summary('networks_selection', 'Network'); + }, 'json'); + } + } + + function networks_select(selected_rows) + { + var selected_ids = new Array(); + for(i=0; i<selected_rows.length; i++) { + selected_ids[i] = selected_rows[i].id; + } + if (selected_ids.length == 1) + { + $('#networks_selection').load('<%= url_for :action => "show" %>', + { id: parseInt(selected_ids[0].substring(3))}); + } + } + +</script> + +<div class="panel_header"></div> +<% if @networks.size != 0 %> + <div class="data_section"> + <%= render :partial => "grid", :locals => { :table_id => "networks_grid", + :networks => @networks, + :on_select => "networks_select"} %> + </div> + + <div class="selection_detail" id="networks_selection"> + <div class="selection_left"> + <div>Select a network.</div> + </div> + </div> +<% else %> + <div class="data_section"> + <div class="no-grid-items"> + <%= image_tag 'no-grid-items.png', :style => 'float: left;' %> + + <div class="no-grid-items-text"> + No networks found. <br/><br/> + <%= image_tag "icon_addhost.png", :style=>"vertical-align:middle;" %> + <a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]">Add first network</a> + </div> + </div> + </div> +<% end %> diff --git a/src/app/views/network/new.rhtml b/src/app/views/network/new.rhtml new file mode 100644 index 0000000..15b7304 --- /dev/null +++ b/src/app/views/network/new.rhtml @@ -0,0 +1,28 @@ +<%- content_for :title do -%> + Add Network +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + + <!-- DIALOG BODY --> + <form method="POST" action="<%= url_for :action => 'create' %>" id="network_form" > + <div class="dialog_form"> + <%= render :partial => 'form', :locals => { :create => true } %> + </div> + <!-- DIALOG FOOTER --> + <%= popup_footer("$('#network_form').submit()", "Add Network") %> + </form> + +<script type="text/javascript"> +$(function() { + var networkoptions = { + target: '<%= url_for :action => 'create' %>', + dataType: 'json', + success: afterNetwork // post-submit callback + }; + + // bind form using 'ajaxForm' + $('#network_form').ajaxForm(networkoptions); +}); +</script> + diff --git a/src/app/views/network/new_bonding.rhtml b/src/app/views/network/new_bonding.rhtml new file mode 100644 index 0000000..9ee6994 --- /dev/null +++ b/src/app/views/network/new_bonding.rhtml @@ -0,0 +1,32 @@ +<form method="POST" + action="<%= url_for :action => 'create_bonding' %>" id="bonding_form" > + + <div id="selected_popup_content_header"> + Create Bonded Interface + </div> + + <%= render :partial => 'bonding_form' %> +</form> + +<%= multi_button_popup_footer({"Create Bonding" => + "$('#bonding_form').submit()"}) %> + +<script type="text/javascript"> +$(function() { + var bonding_options = { + target: '<%= url_for :action => 'create_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind forms using 'ajaxForm' + $('#bonding_form').ajaxForm(bonding_options); + +}); +</script> diff --git a/src/app/views/network/new_ip_address.rhtml b/src/app/views/network/new_ip_address.rhtml new file mode 100644 index 0000000..df2a2bb --- /dev/null +++ b/src/app/views/network/new_ip_address.rhtml @@ -0,0 +1,44 @@ +<% if @parent_type == 'network' %> +<form method="POST" + action="<%= url_for :action => 'create_ip_address' %>" + id="ip_address_form" > +<% end %> + + <div id="selected_popup_content_header"> + Create Ip Address + </div> + + <%= render :partial => 'ip_address_form' %> + +<% if @parent_type == 'network' %> +</form> +<% end %> + +<% if @parent_type == 'network' %> +<%= multi_button_popup_footer({"Create IP Address" => + "$('#ip_address_form').submit()"}) %> +<% end %> + +<script type="text/javascript"> +$(function() { + var ip_address_options = { + target: '<%= url_for :action => 'create_ip_address' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + <% if @parent_type == 'network' %> + refresh_summary('networks_selection', + '<%= url_for :controller => "network", :action => "show" %>', + <%= @network.id %>); + <% end %> + } + } + }; + + // bind forms using 'ajaxForm' + $('#ip_address_form').ajaxForm(ip_address_options); +}); +</script> diff --git a/src/app/views/network/show.rhtml b/src/app/views/network/show.rhtml new file mode 100644 index 0000000..2ac68ea --- /dev/null +++ b/src/app/views/network/show.rhtml @@ -0,0 +1,31 @@ +<%- content_for :title do -%> + <%=h @network.name %> +<%- end -%> + +<%- content_for :action_links do -%> + <%= link_to image_tag("icon_edit.png") + "Edit", + {:action => 'edit', :id => @network.id }, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> + + <%= link_to image_tag("icon_edit.png") + "Edit IP Addresses", + {:action => 'edit_network_ip_addresses', :id => @network.id }, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> +<%- end -%> + +<div id="hosts_selection_id" style="display:none"><%= @host.id %></div> +<div class="selection_key"> + Name:<br/> + Type:<br/> + Boot Type:<br/> + IP Addresses:<br/> +</div> +<div class="selection_value"> + <%=h @network.name %><br/> + <%=h @network.type %><br/> + <%=h @network.boot_type.label %><br/> + <%=@network.ip_addresses.collect{ |ip| + ip.address}.join("<br/>") %> +</div> + +<%- content_for :right do -%> +<%- end -%> diff --git a/src/app/views/nic/_list.rhtml b/src/app/views/nic/_list.rhtml index 07f7e44..eb3a4a7 100644 --- a/src/app/views/nic/_list.rhtml +++ b/src/app/views/nic/_list.rhtml @@ -1,7 +1,6 @@ <table class='listing'> <thead> <tr> - <th class="empty">IP</th> <th>MAC</th> <th>Bridge</th> <th>Usage Type</th> @@ -11,7 +10,6 @@ </thead> <% for nic in nics %> <tr class="<%= cycle('odd','even', :name => nics) %>"> - <td><%= link_to nic.ip_addr, { :controller => "nic", :action => 'show', :id => nic }, { :class => "show" } %></td> <td><%= nic.mac %></td> <td><%= nic.bridge %></td> <td><%= nic.usage_type %></td> diff --git a/src/db/migrate/028_refactor_networking_model.rb b/src/db/migrate/028_refactor_networking_model.rb new file mode 100644 index 0000000..946b976 --- /dev/null +++ b/src/db/migrate/028_refactor_networking_model.rb @@ -0,0 +1,271 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# introduce networks and ip_addresses tables, refactor relationships +class RefactorNetworkingModel < ActiveRecord::Migration + def self.up + + #################################################### + # bugfix, bridge tables shouldn't have their own ids + remove_column :bondings_nics, :id + + ################################################################## + # add networks, usages tables and networks_usage_types bridge + create_table :networks do |t| + t.string :type, :null => false + t.string :name, :null => false + t.integer :boot_type_id, :null => false + + # attributes for Vlan (type=Vlan) + t.integer :number + end + + create_table :usages do |t| + t.string :label, :null => false + t.string :usage, :null => false + end + + create_table :networks_usages, :id => false do |t| + t.integer :network_id, :null => false + t.integer :usage_id, :null => false + end + + add_index :networks_usages, [:network_id, :usage_id], :unique => true + + # create usages + Usage.create(:label => 'Guest', :usage => 'guest') + Usage.create(:label => 'Management', :usage => 'management') + Usage.create(:label => 'Storage', :usage => 'storage') + + # referential integrity for networks tables + execute "alter table networks add constraint + fk_network_boot_types + foreign key (boot_type_id) references + boot_types(id)" + execute "alter table networks_usages add constraint + fk_networks_usages_network_id + foreign key (network_id) references + networks(id)" + execute "alter table networks_usages add constraint + fk_networks_usages_usage_id + foreign key (usage_id) references + usages(id)" + + # add foreign keys to nics / bondings table + add_column :nics, :physical_network_id, :integer + add_column :bondings, :vlan_id, :integer + + # referential integrity for nic/bondings network ids + execute "alter table nics add constraint + fk_nic_networks + foreign key (physical_network_id) references + networks(id)" + execute "alter table bondings add constraint + fk_bonding_networks + foreign key (vlan_id) references + networks(id)" + + #################################################### + # create ip_addresses table + create_table :ip_addresses do |t| + t.string :type + + # foreign keys to associated entities + t.integer :nic_id + t.integer :bonding_id + t.integer :network_id + + # common attributes + t.string :address, :limit => 39, :null => false + t.string :gateway, :limit => 39 + + # attributes for IPv4 (type=IpV4Address) + t.string :netmask, :limit => 15 + t.string :broadcast, :limit => 15 + + # attributes for IPv6 (type=IpV6Address) + t.string :prefix, :limit => 39 + t.timestamps + end + + # referential integrity for ip_addresses table + execute "alter table ip_addresses add constraint + fk_nic_ip_addresses + foreign key (nic_id) references nics(id)" + execute "alter table ip_addresses add constraint + fk_bonding_ip_addresses + foreign key (bonding_id) references bondings(id)" + execute "alter table ip_addresses add constraint + fk_network_ip_addresses + foreign key (network_id) references networks(id)" + + ################################################################### + static_boot_type_id + BootType.find(:first, + :conditions => {:proto => 'static'} ).id + + # migrate nic ip_addresses to networks / ip_addresses table + i = 0 + Nic.find(:all).each do |nic| + if nic.boot_type_id == static_boot_type_id + IpV4Address.new(:nic_id => nic.id, + :address => nic.ip_addr).save! + + end + network = PhysicalNetwork.new( + :name => 'Physical Network ' + i.to_s, + :boot_type_id => nic.boot_type_id) + network.save! + + ip_address = IpV4Address.new(:address => nic.ip_addr ? nic.ip_addr : '0.0.0.0', + :netmask => nic.netmask, + :broadcast => nic.broadcast, + :gateway => nic.ip_addr) + ip_address.network = network + ip_address.save! + + nic.physical_network = network + nic.save! + + i += 1 + end + + # migrate bonding ip_addresses to networks / ip_addresses table + i = 0 + Bonding.find(:all).each do |bonding| + if bonding.boot_type_id == static_boot_type_id + IpV4Address.new(:bonding_id => bonding.id, + :address => bonding.ip_addr).save! + end + network = Vlan.new( + :name => 'VLAN ' + i.to_s, + :number => i, + :boot_type_id => bonding.boot_type_id) + network.save! + + ip_address = IpV4Address.new(:address => bonding.ip_addr ? bonding.ip_addr : '0.0.0.0', + :netmask => bonding.netmask, + :broadcast => bonding.broadcast, + :gateway => bonding.ip_addr) + ip_address.network = network + ip_address.save! + + bonding.vlan = network + bonding.save! + + i += 1 + end + + ############################################################## + # remove nics / bonding ip address and network related columns + remove_column :nics, :ip_addr + remove_column :nics, :netmask + remove_column :nics, :broadcast + remove_column :nics, :boot_type_id + remove_column :bondings, :ip_addr + remove_column :bondings, :netmask + remove_column :bondings, :broadcast + remove_column :bondings, :boot_type_id + + + end + + def self.down + ############################################################## + # readd nics / bonding ip address related columns + add_column :nics, :ip_addr, :string, :limit => 16 + add_column :nics, :netmask, :string, :limit => 16 + add_column :nics, :broadcast, :string, :limit => 16 + add_column :nics, :boot_type_id, :integer + add_column :bondings, :ip_addr, :string, :limit => 16 + add_column :bondings, :netmask, :string, :limit => 16 + add_column :bondings, :broadcast, :string, :limit => 16 + add_column :bondings, :boot_type_id, :integer + + execute "alter table nics add constraint + fk_nic_boot_types + foreign key (boot_type_id) references + boot_types(id)" + execute "alter table bondings add constraint + fk_bonding_boot_types + foreign key (boot_type_id) references + boot_types(id)" + + ############################################################## + # attempt to migrate ip information back into nics table. + # because a nic can have multiple ips (if statically + # assigned) as well as its network, just use the 1st + # found + Nic.find(:all).each do |nic| + if nic.physical_network.ip_addresses.size > 0 + # use the 1st configured network ip + nic.ip_addr = nic.physical_network.ip_addresses[0].address + nic.netmask = nic.physical_network.ip_addresses[0].netmask + nic.broadcast = nic.physical_network.ip_addresses[0].broadcast + end + + if nic.ip_addresses.size > 0 + # use the 1st assigned static ip + nic.ip_addr = nic.ip_addresses[0].address + end + + nic.boot_type_id = nic.physical_network.boot_type_id + + nic.save! + end + + # attempt to migrate ip information back into bondings table. + # because a bonding can have multiple ips (if statically + # assigned) as well as its network, just use the 1st + # found + Bonding.find(:all).each do |bonding| + if bonding.vlan.ip_addresses.size > 0 + # use the 1st configured network ip + bonding.ip_addr = bonding.vlan.ip_addresses[0].address + bonding.netmask = bonding.vlan.ip_addresses[0].netmask + bonding.broadcast = bonding.vlan.ip_addresses[0].broadcast + end + + if bonding.ip_addresses.size > 0 + # use the 1st assigned static ip + bonding.ip_addr = bonding.ip_addresses[0].address + end + + bonding.boot_type_id = bonding.vlan.boot_type_id + + bonding.save! + end + + ############################################################## + # drop ip_addresses table + drop_table :ip_addresses + + # drop network ids from nics / bondings table + remove_column :nics, :physical_network_id + remove_column :bondings, :vlan_id + + # drop networks tables + drop_table :networks_usages + drop_table :usages + drop_table :networks + + ############################################################## + # undo bugfix above + add_column :bondings_nics, :id, :integer + end +end diff --git a/src/public/javascripts/ovirt.js b/src/public/javascripts/ovirt.js index 4579c80..d84b3d9 100644 --- a/src/public/javascripts/ovirt.js +++ b/src/public/javascripts/ovirt.js @@ -297,4 +297,23 @@ function delete_pool(delete_url, id) $.jGrowl(data.alert); } }, 'json'); -} \ No newline at end of file +} + + +function get_selected_networks() +{ + return get_selected_checkboxes("networks_grid_form"); +} + +function afterNetwork(response, status){ + ajax_validation(response, status); + if (response.success) { + $(document).trigger('close.facebox'); + grid = $("#networks_grid"); + if (grid.size()>0 && grid != null) { + grid.flexReload(); + } else { + $tabs.tabs("load",$tabs.data('selected.tabs')); + } + } +} diff --git a/src/public/stylesheets/components.css b/src/public/stylesheets/components.css index 16eaf62..228ff7b 100644 --- a/src/public/stylesheets/components.css +++ b/src/public/stylesheets/components.css @@ -267,4 +267,52 @@ .detail-pane-chart { height: 50px; width: 375px; -}; +} + + +/************************* + * new popup components + *************************/ +.popup-content-selection { + float: left; + width: 45%; + padding-left: 20px; + padding-top: 10px; +} + +.popup-content-selection select{ + min-width: 200px; +} + +.popup-content-footer{ + width: 99%; + float: left; +} + +.selected_popup_content { + padding-left: 20px; + min-height: 50px; + float: left; + width: 96%; +} + +#selected_popup_content_header { + padding-bottom: 5px; + font-weight: bold; +} + +#selected_popup_content_expanded{ + padding-bottom: 50px; +} + +.selected_popup_content_left { + float: left; + width: 40%; + padding-bottom: 15px; +} + +.selected_popup_content_right { + float: left; + width: 40%; + padding-bottom: 15px; +} -- 1.5.6.5
Mohammed Morsi
2008-Oct-31 23:51 UTC
[Ovirt-devel] [PATCH server] network integration into ovirt server db and wui
--- src/app/controllers/host_controller.rb | 14 +- src/app/controllers/network_controller.rb | 419 ++++++++++++++++++++ src/app/helpers/application_helper.rb | 28 +- src/app/helpers/network_helper.rb | 2 + src/app/models/bonding.rb | 30 +- src/app/models/ip_address.rb | 27 ++ src/app/models/ip_v4_address.rb | 53 +++ src/app/models/ip_v6_address.rb | 46 +++ src/app/models/network.rb | 32 ++ src/app/models/nic.rb | 21 +- src/app/models/physical_network.rb | 21 + src/app/models/usage.rb | 21 + src/app/models/vlan.rb | 24 ++ src/app/views/dashboard/index.html.erb | 7 + src/app/views/host/edit_network.rhtml | 85 ++++ src/app/views/host/show.rhtml | 30 +- src/app/views/network/_bonding_form.rhtml | 46 +++ src/app/views/network/_form.rhtml | 46 +++ src/app/views/network/_grid.rhtml | 37 ++ src/app/views/network/_ip_address_form.rhtml | 62 +++ src/app/views/network/_ip_addresses_form.rhtml | 50 +++ src/app/views/network/_select.rhtml | 32 ++ src/app/views/network/edit.rhtml | 34 ++ src/app/views/network/edit_bonding.rhtml | 60 +++ src/app/views/network/edit_ip_address.rhtml | 76 ++++ .../views/network/edit_network_ip_addresses.rhtml | 13 + src/app/views/network/edit_nic.rhtml | 62 +++ src/app/views/network/list.html.erb | 72 ++++ src/app/views/network/new.rhtml | 28 ++ src/app/views/network/new_bonding.rhtml | 36 ++ src/app/views/network/new_ip_address.rhtml | 44 ++ src/app/views/network/show.rhtml | 31 ++ src/app/views/nic/_list.rhtml | 2 - src/db/migrate/028_refactor_networking_model.rb | 271 +++++++++++++ src/public/javascripts/ovirt.js | 21 +- src/public/stylesheets/components.css | 50 +++- 36 files changed, 1894 insertions(+), 39 deletions(-) create mode 100644 src/app/controllers/network_controller.rb create mode 100644 src/app/helpers/network_helper.rb create mode 100644 src/app/models/ip_address.rb create mode 100644 src/app/models/ip_v4_address.rb create mode 100644 src/app/models/ip_v6_address.rb create mode 100644 src/app/models/network.rb create mode 100644 src/app/models/physical_network.rb create mode 100644 src/app/models/usage.rb create mode 100644 src/app/models/vlan.rb create mode 100644 src/app/views/host/edit_network.rhtml create mode 100644 src/app/views/network/_bonding_form.rhtml create mode 100644 src/app/views/network/_form.rhtml create mode 100644 src/app/views/network/_grid.rhtml create mode 100644 src/app/views/network/_ip_address_form.rhtml create mode 100644 src/app/views/network/_ip_addresses_form.rhtml create mode 100644 src/app/views/network/_select.rhtml create mode 100644 src/app/views/network/edit.rhtml create mode 100644 src/app/views/network/edit_bonding.rhtml create mode 100644 src/app/views/network/edit_ip_address.rhtml create mode 100644 src/app/views/network/edit_network_ip_addresses.rhtml create mode 100644 src/app/views/network/edit_nic.rhtml create mode 100644 src/app/views/network/list.html.erb create mode 100644 src/app/views/network/new.rhtml create mode 100644 src/app/views/network/new_bonding.rhtml create mode 100644 src/app/views/network/new_ip_address.rhtml create mode 100644 src/app/views/network/show.rhtml create mode 100644 src/db/migrate/028_refactor_networking_model.rb diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb index a40d297..f961d1a 100644 --- a/src/app/controllers/host_controller.rb +++ b/src/app/controllers/host_controller.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -30,7 +30,7 @@ class HostController < ApplicationController end end - before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms] + before_filter :pre_action, :only => [:host_action, :enable, :disable, :clear_vms, :edit_network] # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => [:post, :put], :only => [ :create, :update ], @@ -81,7 +81,7 @@ class HostController < ApplicationController def addhost @hardware_pool = Pool.find(params[:hardware_pool_id]) - render :layout => 'popup' + render :layout => 'popup' end def add_to_smart_pool @@ -156,6 +156,14 @@ class HostController < ApplicationController render :json => @json_hash end + def edit_network + render :layout => 'popup' + end + + def bondings_json + bondings = Host.find(params[:id]).bondings + render :json => bondings.collect{ |x| {:id => x.id, :name => x.name} } + end private #filter methods diff --git a/src/app/controllers/network_controller.rb b/src/app/controllers/network_controller.rb new file mode 100644 index 0000000..725e60d --- /dev/null +++ b/src/app/controllers/network_controller.rb @@ -0,0 +1,419 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. +# + +class NetworkController < ApplicationController + ########################## Networks related actions + + def network_permissions + # TODO more robust permission system + # either by subclassing network from pool + # or by extending permission model to accomodate + # any object + @default_pool = HardwarePool.get_default_pool + set_perms(@default_pool) + unless @can_modify + flash[:notice] = 'You do not have permission to view networks' + redirect_to :controller => 'dashboard' + end + end + + def list + @networks = Network.find(:all) + network_permissions + end + + def networks_json + json_list(Network.find(:all), [:id, :name, :type, [:boot_type, :label]]) + end + + def show + @network = Network.find(params[:id]) + network_permissions + respond_to do |format| + format.html { render :layout => 'selection' } + format.xml { render :xml => @network.to_xml } + end + end + + def new + @boot_types = BootType.find(:all) + @usage_types = Usage.find(:all) + render :layout => 'popup' + end + + def create + begin + @network = PhysicalNetwork.new(params[:network]) if params[:network][:type] == 'PhysicalNetwork' + @network = Vlan.new(params[:network]) if params[:network][:type] == 'Vlan' + @network.save! + alert = "Network was successfully created." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue + render :json => { :object => "network", :success => false, + :errors => + @network.errors.localize_error_messages.to_a } + end + end + + def edit + @network = Network.find(params[:id]) + @usage_types = Usage.find(:all) + @boot_types = BootType.find(:all) + render :layout => 'popup' + end + + def update + begin + @network = Network.find(params[:id]) + if @network.type == 'PhysicalNetwork' + @network = PhysicalNetwork.find(params[:id]) + else + @network = Vlan.find(params[:id]) + end + + @network.usages.delete_all + @network.update_attributes!(params[:network]) + + alert = "Network was successfully updated." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue Exception => e + render :json => { :object => "network", :success => false, + :errors => + @network.errors.localize_error_messages.to_a } + end + end + + def delete + failed_networks = [] + networks_ids_str = params[:network_ids] + network_ids = networks_ids_str.split(",").collect {|x| x.to_i} + network_ids.each{ |x| + network = Network.find(x) + unless network.type.to_s == 'Vlan' && + Vlan.find(x).bondings.size != 0 || + network.type.to_s == 'PhysicalNetwork' && + PhysicalNetwork.find(x).nics.size != 0 + begin + Network.destroy(x) + rescue + failed_networks.push x + end + else + failed_networks.push x + end + } + if failed_networks.size == 0 + render :json => { :object => "network", + :success => true, + :alert => "Successfully deleted networks" } + else + render :json => { :object => "network", + :success => false, + :alert => "Failed deleting " + + failed_networks.size.to_s + + " networks due to existing bondings/nics" } + end + end + + def edit_network_ip_addresses + @network = Network.find(params[:id]) + render :layout => 'popup' + end + + + ########################## Ip Address related actions + + def ip_addresses_json + @parent_type = params[:parent_type] + if @parent_type == 'network' + ip_addresses = Network.find(params[:id]).ip_addresses + elsif @parent_type == 'nic' + ip_addresses = Nic.find(params[:id]).ip_addresses + elsif @parent_type == 'bonding' and params[:id] + ip_addresses = Bonding.find(params[:id]).ip_addresses + else + ip_addresses = [] + end + + ip_addresses_json = [] + ip_addresses.each{ |x| + ip_addresses_json.push({:id => x.id, :name => x.address}) } + render :json => ip_addresses_json + end + + def new_ip_address + @parent_type = params[:parent_type] + @network = Network.find(params[:id]) if @parent_type == 'network' + @nic = Nic.find(params[:id]) if @parent_type == 'nic' + @bonding = Bonding.find(params[:id]) if @parent_type == 'bonding' and params[:id] + + render :layout => false + end + + def _create_ip_address + if params[:ip_address][:type] == "IpV4Address" + @ip_address = IpV4Address.new(params[:ip_address]) + else + @ip_address = IpV6Address.new(params[:ip_address]) + end + @ip_address.save! + end + + def create_ip_address + begin + _create_ip_address + alert = "Ip Address was successfully created." + render :json => { :object => "ip_address", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a } + end + end + + def edit_ip_address + @ip_address = IpAddress.find(params[:id]) + + @parent_type = params[:parent_type] + @network = @ip_address.network if @ip_address.network_id + @nic = @ip_address.nic if @ip_address.nic_id + @bonding = @ip_address.bonding if @ip_address.bonding_id + + render :layout => false + end + + def _update_ip_address(id) + @ip_address = IpAddress.find(id) + + # special case if we are switching types + if @ip_address.type != params[:ip_address][:type] + if params[:ip_address][:type] == 'IpV4Address' + @ip_address = IpV4Address.new(params[:ip_address]) + @ip_address.save! + IpV6Address.delete(id) + else + @ip_address = IpV6Address.new(params[:ip_address]) + @ip_address.save! + IpV4Address.delete(id) + end + else + if @ip_address.type == 'IpV4Address' + @ip_address = IpV4Address.find(id) + else + @ip_address = IpV6Address.find(id) + end + @ip_address.update_attributes!(params[:ip_address]) + end + + end + + def update_ip_address + begin + _update_ip_address(params[:id]) + alert = "IpAddress was successfully updated." + render :json => { :object => "network", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a } + end + end + + def destroy_ip_address + begin + IpAddress.delete(params[:id]) + alert = "Ip Address was successfully deleted." + render :json => { :object => "ip_address", :success => true, + :alert => alert } + rescue + render :json => { :object => "ip_address", :success => false, + :alert => 'Ip Address Deletion Failed' } + end + end + + + ########################## NICs related actions + + def edit_nic + @nic = Nic.find(params[:id]) + @network = @nic.physical_network + + @networks = PhysicalNetwork.find(:all) + network_options + + render :layout => false + end + + def update_nic + begin + network_options + @network = Network.find(params[:nic][:physical_network_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @nic = Nic.find(params[:id]) + @nic.update_attributes!(params[:nic]) + + alert = "Nic was successfully updated." + render :json => { :object => "nic", :success => true, + :alert => alert } + rescue Exception => e + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "nic", :success => false, + :errors => + @nic.errors.localize_error_messages.to_a } + end + end + end + + ########################## Bonding related actions + + def new_bonding + unless params[:host_id] + flash[:notice] = "Host is required." + redirect_to :controller => 'dashboard' + end + + @host = Host.find(params[:host_id]) + @networks = Vlan.find(:all) + network_options + + render :layout => false + end + + def create_bonding + begin + network_options + @network = Network.find(params[:bonding][:vlan_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @bonding = Bonding.new(params[:bonding]) + @bonding.ip_addresses << @ip_address if @ip_address + @bonding.save! + + if @ip_address + @ip_address.bonding_id = @bonding.id + @ip_address.save! + end + + alert = "Bonding was successfully created." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "bonding", :success => false, + :errors => + @bonding.errors.localize_error_messages.to_a } + end + end + end + + def edit_bonding + @bonding = Bonding.find(params[:id]) + @network = @bonding.vlan + + @host = @bonding.host + @networks = Vlan.find(:all) + network_options + + render :layout => false + end + + def update_bonding + begin + network_options + @network = Network.find(params[:bonding][:vlan_id]) + + if @network.boot_type.id == @static_boot_type.id + if params[:ip_address][:id] == "New" + _create_ip_address + elsif params[:ip_address][:id] != "" + _update_ip_address(params[:ip_address][:id]) + end + end + + @bonding = Bonding.find(params[:id]) + @bonding.nics.delete_all + @bonding.update_attributes!(params[:bonding]) + + alert = "Bonding was successfully updated." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + if @ip_address and @ip_address.errors.size != 0 + render :json => { :object => "ip_address", :success => false, + :errors => + @ip_address.errors.localize_error_messages.to_a} + else + render :json => { :object => "bonding", :success => false, + :errors => + @bonding.errors.localize_error_messages.to_a } + end + end + end + + def destroy_bonding + begin + Bonding.destroy(params[:id]) + alert = "Bonding was successfully deleted." + render :json => { :object => "bonding", :success => true, + :alert => alert } + rescue + render :json => { :object => "bonding", :success => false, + :alert => 'Bonding Deletion Failed' } + end + + end + + + ########################## Misc methods + + protected + def network_options + @bonding_types = BondingType.find(:all) + @static_boot_type = BootType.find(:first, + :conditions => { :proto => 'static' }) + end + +end diff --git a/src/app/helpers/application_helper.rb b/src/app/helpers/application_helper.rb index d7b6628..0178ad0 100644 --- a/src/app/helpers/application_helper.rb +++ b/src/app/helpers/application_helper.rb @@ -84,21 +84,31 @@ module ApplicationHelper } end - def popup_footer(action, label) - %{ - <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0;"> + # expects hash of labels => actions + def multi_button_popup_footer(buttons = {}) + buttons_html = "" + buttons.each{ |label, action| + buttons_html+="<div class=\"button\">" + + " <div class=\"button_left_blue\"></div>" + + " <div class=\"button_middle_blue\"><a href=\"#\" onclick=\"#{action}\">#{label}</a></div>" + + " <div class=\"button_right_blue\"></div>" + + "</div>" + } + + %{ + <div style="background: url(#{image_path "fb_footer.jpg"}) repeat-x; height: 37px; text-align:right; padding: 9px 9px 0 0; float: left; width: 97%;"> <div class="button"> <div class="button_left_grey"></div> <div class="button_middle_grey"><a href="#" onclick="$(document).trigger('close.facebox')">Cancel</a></div> <div class="button_right_grey"></div> </div> - <div class="button"> - <div class="button_left_blue"></div> - <div class="button_middle_blue"><a href="#" onclick="#{action}">#{label}</a></div> - <div class="button_right_blue"></div> - </div> + #{buttons_html} </div> - } + } + end + + def popup_footer(action, label) + multi_button_popup_footer({label => action}) end def ok_footer diff --git a/src/app/helpers/network_helper.rb b/src/app/helpers/network_helper.rb new file mode 100644 index 0000000..ebbce0b --- /dev/null +++ b/src/app/helpers/network_helper.rb @@ -0,0 +1,2 @@ +module NetworkHelper +end diff --git a/src/app/models/bonding.rb b/src/app/models/bonding.rb index 006c261..c9af38c 100644 --- a/src/app/models/bonding.rb +++ b/src/app/models/bonding.rb @@ -30,6 +30,16 @@ # interface. They can be ignored if not used. # class Bonding < ActiveRecord::Base + belongs_to :host + belongs_to :bonding_type + belongs_to :vlan + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :nics, + :join_table => 'bondings_nics', + :foreign_key => :bonding_id + + validates_presence_of :name, :message => 'A name is required.' @@ -42,18 +52,20 @@ class Bonding < ActiveRecord::Base validates_presence_of :interface_name, :message => 'An interface name is required.' - validates_presence_of :boot_type_id, - :message => 'A boot type must be specified.' - validates_presence_of :bonding_type_id, :message => 'A bonding type must be specified.' - belongs_to :host - belongs_to :bonding_type - belongs_to :boot_type + validates_presence_of :vlan_id, + :message => 'A vlan must be specified.' + + protected + def validate + if vlan.boot_type.proto == 'static' and ip_addresses.size == 0 + errors.add("vlan_id", + "is static. Must create at least one static ip") + end + + end - has_and_belongs_to_many :nics, - :join_table => 'bondings_nics', - :foreign_key => :bonding_id end diff --git a/src/app/models/ip_address.rb b/src/app/models/ip_address.rb new file mode 100644 index 0000000..5d2e6af --- /dev/null +++ b/src/app/models/ip_address.rb @@ -0,0 +1,27 @@ +# ip_address.rb +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpAddress+ is the base class for all address related classes. +# +class IpAddress < ActiveRecord::Base + # one of these 3 will apply for each address + belongs_to :network + belongs_to :nic + belongs_to :bonding +end diff --git a/src/app/models/ip_v4_address.rb b/src/app/models/ip_v4_address.rb new file mode 100644 index 0000000..f3128a4 --- /dev/null +++ b/src/app/models/ip_v4_address.rb @@ -0,0 +1,53 @@ +# ip_v4_address.rb +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com>, +# Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpV4Address+ represents a single IPv4 address. +# +class IpV4Address < IpAddress + ADDRESS_TEST = %r{^(\d{1,3}\.){3}\d{1,3}$} + + validates_presence_of :address, + :message => 'An address must be supplied.' + validates_format_of :address, + :with => ADDRESS_TEST + + validates_presence_of :netmask, + :message => 'A netmask must be supplied.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :netmask, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + + validates_presence_of :gateway, + :message => 'A gateway address must be supplied.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :gateway, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + + validates_presence_of :broadcast, + :message => 'A broadcast address must be supplied.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :broadcast, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + +end diff --git a/src/app/models/ip_v6_address.rb b/src/app/models/ip_v6_address.rb new file mode 100644 index 0000000..11951cc --- /dev/null +++ b/src/app/models/ip_v6_address.rb @@ -0,0 +1,46 @@ +# ip_v6_address.rb +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com>, +# Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# +IpV6Address+ represents a single IPv6 address. +# +class IpV6Address < IpAddress + ADDRESS_TEST = %r{^([0-9a-fA-F]{0,4}|0)(\:([0-9a-fA-F]{0,4}|0)){7}$} + + validates_presence_of :address, + :message => 'An address must be provided.' + validates_format_of :address, + :with => ADDRESS_TEST + + validates_presence_of :gateway, + :message => 'A gateway address must be provided.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :gateway, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + + validates_presence_of :prefix, + :message => 'A prefix must be provided.', + :if => Proc.new { |ip| ip.network_id != nil } + validates_format_of :prefix, + :with => ADDRESS_TEST, + :if => Proc.new { |ip| ip.network_id != nil } + +end diff --git a/src/app/models/network.rb b/src/app/models/network.rb new file mode 100644 index 0000000..404633d --- /dev/null +++ b/src/app/models/network.rb @@ -0,0 +1,32 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Network < ActiveRecord::Base + belongs_to :boot_type + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :usages, :join_table => 'networks_usages' + + validates_presence_of :type, + :message => 'A type must be specified.' + validates_presence_of :name, + :message => 'A name must be specified.' + validates_presence_of :boot_type_id, + :message => 'A boot type must be specified.' + +end diff --git a/src/app/models/nic.rb b/src/app/models/nic.rb index baf7095..5649763 100644 --- a/src/app/models/nic.rb +++ b/src/app/models/nic.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -19,7 +19,22 @@ class Nic < ActiveRecord::Base belongs_to :host - belongs_to :boot_type + belongs_to :physical_network + has_many :ip_addresses, :dependent => :destroy + + has_and_belongs_to_many :bondings, :join_table => 'bondings_nics' + + validates_presence_of :host_id, + :message => 'A host must be specified.' + + validates_presence_of :physical_network_id, + :message => 'A network must be specified.' - has_and_belongs_to_many :bonding, :join_table => 'bondings_nics' + protected + def validate + if physical_network.boot_type.proto == 'static' and ip_addresses.size == 0 + errors.add("physical_network_id", + "is static. Must create at least one static ip") + end + end end diff --git a/src/app/models/physical_network.rb b/src/app/models/physical_network.rb new file mode 100644 index 0000000..6923e40 --- /dev/null +++ b/src/app/models/physical_network.rb @@ -0,0 +1,21 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class PhysicalNetwork < Network + has_many :nics +end diff --git a/src/app/models/usage.rb b/src/app/models/usage.rb new file mode 100644 index 0000000..353e8f4 --- /dev/null +++ b/src/app/models/usage.rb @@ -0,0 +1,21 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Usage < ActiveRecord::Base + has_and_belongs_to_many :networks, :join_table => 'networks_usages' +end diff --git a/src/app/models/vlan.rb b/src/app/models/vlan.rb new file mode 100644 index 0000000..f7889f4 --- /dev/null +++ b/src/app/models/vlan.rb @@ -0,0 +1,24 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class Vlan < Network + has_many :bondings + + validates_presence_of :number, + :message => 'A number must be specified.' +end diff --git a/src/app/views/dashboard/index.html.erb b/src/app/views/dashboard/index.html.erb index 92cb4da..8815ebc 100644 --- a/src/app/views/dashboard/index.html.erb +++ b/src/app/views/dashboard/index.html.erb @@ -13,6 +13,13 @@ </table> </div> + <% if @can_modify %> + <h3>Networks</h3> + <a href="<%= url_for :controller => 'network', :action => 'list' %>"> + View / Edit + </a> + <% end %> + </div> <!-- end #tools --> </td> diff --git a/src/app/views/host/edit_network.rhtml b/src/app/views/host/edit_network.rhtml new file mode 100644 index 0000000..7ec3180 --- /dev/null +++ b/src/app/views/host/edit_network.rhtml @@ -0,0 +1,85 @@ +<%- content_for :title do -%> + Edit <%= @host.hostname %> Network Devices +<%- end -%> + +<%- content_for :description do -%> + Select and edit nics and bonded interfaces on <%= @host.hostname %> +<%- end -%> + +<div id="select-host-nic" class="popup-content-selection"> +<%= select_with_label "NICs", "nic", "id", + @host.nics. + collect{ |nic| [nic.mac, nic.id] }. + insert(0, "") %> +</div> + +<div id="select-host-bonding" class="popup-content-selection"> +<%= select_with_label "Bonded Interfaces", "bonding", "id", [] %> +</div> + +<div style="clear: both;"></div> + +<div id="selected_nic_bonding" class="selected_popup_content"></div> + +<div id="host_network_footer" class="popup-content-footer"> + <%= ok_footer %> +</div> + +<script type="text/javascript"> +function reset_nics_bonding_detail(){ + var data='Select NIC or Bonded Interface<br/>'; + + $("#selected_nic_bonding").html(data); + $("#host_network_footer").show(); +}; + +reset_nics_bonding_detail(); // run it once for inital content + +function reset_nics_select(){ + $("#nic_id option:first").attr("selected", true); +}; + +function reset_bonding_select(){ + // incase of new additions / deletions, repopulate select box + $.getJSON( + "<%= url_for :action => 'bondings_json', :id => @host.id %>", + {}, + function(j){ + var options = "<option value=''></option>" + + "<option value='New'>New</option>"; + for(var i = 0; i < j.length; i++){ + options += '<option value="' + j[i].id + '">' + j[i].name + + '</option>'; + } + $("#bonding_id").html(options); + }); + + $("#bonding_id option:first").attr("selected", true); +}; + +reset_bonding_select(); // run it once for initial content + +$("#nic_id").change(function () { + reset_bonding_select(); + if($('#nic_id').val() != ""){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', + :action => 'edit_nic'%>/" + $('#nic_id').val()); + $("#host_network_footer").hide(); + }else{ + reset_nics_bonding_detail(); + } +}); + +$("#bonding_id").change(function () { + reset_nics_select(); + if($('#bonding_id').val() == "New"){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'new_bonding', :host_id => @host.id %>"); + $("#host_network_footer").hide(); + }else if($('#bonding_id').val() != ""){ + $("#selected_nic_bonding").load("<%= url_for :controller => 'network', :action => 'edit_bonding'%>/" + $('#bonding_id').val()); + $("#host_network_footer").hide(); + }else{ + reset_nics_bonding_detail(); + } +}); +</script> diff --git a/src/app/views/host/show.rhtml b/src/app/views/host/show.rhtml index d1b05ad..b671578 100644 --- a/src/app/views/host/show.rhtml +++ b/src/app/views/host/show.rhtml @@ -6,17 +6,21 @@ <%if @host.disabled? -%> <a href="#" onClick="host_action('enable')"> <%= image_tag "icon_start.png" %> Enable Host - </a> + </a> <% else -%> <a href="#" onClick="host_action('disable')"> <%= image_tag "icon_suspend.png" %> Disable Host - </a> + </a> <% end -%> <%if @host.is_clear_task_valid? -%> <a href="#" onClick="host_action('clear_vms')"> <%= image_tag "icon_x.png" %> Clear VMs - </a> + </a> <% end -%> + <%= link_to image_tag("icon_edit.png") +"Edit Network", + {:controller => 'host', + :action => 'edit_network', :id => @host.id}, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> <%- end -%> <%- end -%> <script type="text/javascript"> @@ -25,8 +29,8 @@ $.post('<%= url_for :controller => "host", :action => 'host_action', :id => @host%>', { action_type: action }, function(data,status){ - refresh_summary('hosts_selection', - '<%= url_for :controller => "host", + refresh_summary('hosts_selection', + '<%= url_for :controller => "host", :action => "show" %>', <%= @host.id %>) $("#hosts_grid").flexReload() @@ -39,14 +43,16 @@ <div id="hosts_selection_id" style="display:none"><%= @host.id %></div> <div class="selection_key"> - UUID:<br/> - CPUs:<br/> - Speed:<br/> - Memory:<br/> - Architecture:<br/> - Hypervisor Type:<br/> + UUID:<br/> + CPUs:<br/> + Speed:<br/> + Memory:<br/> + Architecture:<br/> + Hypervisor Type:<br/> Status:<br/> VMs:<br/> + NICs:<br/> + Bondings:<br/> </div> <div class="selection_value"> <%=h @host.uuid %><br/> @@ -56,6 +62,8 @@ <%=h @host.arch %><br/> <%=h @host.hypervisor_type %><br/> <%=h @host.status_str %><br/> + <%= @host.nics.collect{ |n| n.mac }.join("<br/>") %><br/> + <%= @host.bondings.collect { |n| n.name }.join("<br/>") %><br/> <%= @host.vms.collect{|x| x.uuid }.join(" <br/> ") %><br/> <!-- FIXME: need styling diff --git a/src/app/views/network/_bonding_form.rhtml b/src/app/views/network/_bonding_form.rhtml new file mode 100644 index 0000000..4ec0df1 --- /dev/null +++ b/src/app/views/network/_bonding_form.rhtml @@ -0,0 +1,46 @@ + <%= error_messages_for 'bonding' %> + <%= error_messages_for 'ip_address' %> + +<div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'bonding[host_id]', @host.id %> + + <div class="selected_popup_content_left">Name</div> + <div class="selected_popup_content_right"> + <%= text_field_with_label "", "bonding", "name" %> + </div> + + <div class="selected_popup_content_left">Interface Name</div> + <div class="selected_popup_content_right"> + <%= text_field_with_label "", "bonding", "interface_name" %> + </div> + + <div class="selected_popup_content_left">Bonding Type</div> + <div class="selected_popup_content_right"> + <%= select "bonding", "bonding_type_id", + @bonding_types.collect { |bt| [bt.label, bt.id ] } %> + </div> + + <% if @host.nics.size != 0 %> + <div class="selected_popup_content_left">NICs</div> + <div class="selected_popup_content_right"> + <select id="bonding_nic_ids" name="bonding[nic_ids][]" multiple="true"> + <%= options_from_collection_for_select @host.nics, "id", "mac", + @bonding ? @bonding.nics.collect{ |x| x.nic_id.to_i } : [] %> + </select> + </div> + <% end %> + + <%= render :partial => 'select', :locals => { :target => 'bonding' } %> + +<div id="static_ip_options" + style="<% unless ((@network && @network.boot_type_id == @static_boot_type.id) || + (!@network && @networks.size > 0 && + @networks[0].boot_type_id == @static_boot_type.id)) %> + display: none;<%end %>"> + <%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'bonding', + :parent_id => @bonding ? @bonding.id : nil } %> +</div> + + +</div> diff --git a/src/app/views/network/_form.rhtml b/src/app/views/network/_form.rhtml new file mode 100644 index 0000000..6c67a0e --- /dev/null +++ b/src/app/views/network/_form.rhtml @@ -0,0 +1,46 @@ +<%= error_messages_for 'network' %> + +<!--[form:network]--> +<%= hidden_field 'network', 'id' if @create %> + +<%= text_field_with_label "Name:", "network", "name", + {:style=>"width:250px;"} %> + +<%= select_with_label "Boot Type:", 'network', 'boot_type_id', + @boot_types.collect{ |bt| [bt.label, bt.id] }, + :style=>"width:250px;" %> + +<% if create %> +<%= select_with_label "Type", "network", "type", + [[ "Physical Network", "PhysicalNetwork" ], + [ "VLAN", "Vlan" ] ] %> +<% end %> + + +<div class="field_title">Usage:</div> +<div class="form_field"> + <select id="network_usages_ids" name="network[usage_ids][]" multiple="true"> + <%= options_from_collection_for_select @usage_types, "id", "label", + @network ? @network.usages.collect{ |x| x.id.to_i } : [] %> + </select> +</div> + +<div id="vlan_options" + <% if @network && @network.type != 'Vlan' %> + style="display: none;" + <% end %> +> +<%= text_field_with_label "Number:", "network", "number", + {:style=>"width:250px;"} %> +</div> + + +<script type="text/javascript"> +$("#network_type").change(function () { + if($('#network_type').val() == "Vlan"){ + $("#vlan_options").show(); + }else{ + $("#vlan_options").hide(); + } +}).trigger('change'); +</script> diff --git a/src/app/views/network/_grid.rhtml b/src/app/views/network/_grid.rhtml new file mode 100644 index 0000000..6af0c05 --- /dev/null +++ b/src/app/views/network/_grid.rhtml @@ -0,0 +1,37 @@ +<% networks_per_page = 40 unless (defined? networks_per_page) and !(networks_per_page.nil?) %> +<% usepager = @networks.size > networks_per_page %> + +<div id="<%= table_id %>_div"> +<form id="<%= table_id %>_form"> +<table id="<%= table_id %>" style="display:none"></table> +</form> +</div> +<script type="text/javascript"> + $("#<%= table_id %>").flexigrid + ( + { + url: '<%= url_for :action => "networks_json" %>', + dataType: 'json', + colModel : [ + {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, + {display: 'Name', name: 'name', width : 180, align: 'left'}, + {display: 'Type', name: 'type', width : 180, align: 'left'}, + {display: 'Boot Type', name: 'boot_type', width : 180, align: 'left'} + ], + sortname: "name", + sortorder: "asc", + usepager: <%= usepager %>, + useRp: <%= usepager %>, + rp: <%= networks_per_page %>, + showTableToggleBtn: true, + onSelect: <%= on_select %> + } + ); + function <%= table_id %>checkbox(celDiv) + { + $(celDiv).html('<input type="checkbox" name="grid_checkbox'+$(celDiv).html()+'" class="grid_checkbox" value="'+$(celDiv).html()+'"/>'); + } + +</script> + + diff --git a/src/app/views/network/_ip_address_form.rhtml b/src/app/views/network/_ip_address_form.rhtml new file mode 100644 index 0000000..b952807 --- /dev/null +++ b/src/app/views/network/_ip_address_form.rhtml @@ -0,0 +1,62 @@ +<%= error_messages_for 'ip_address' %> + +<div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'parent_type', @parent_type%> + <%= hidden_field_tag 'ip_address[id]', @ip_address.id if @ip_address%> + <%= hidden_field_tag 'ip_address[network_id]', @network.id if @network %> + <%= hidden_field_tag 'ip_address[nic_id]', @nic.id if @nic %> + <%= hidden_field_tag 'ip_address[bonding_id]', @bonding.id if @bonding %> + + <div class="selected_nic_bonding_left">Type:</div> + <div class="selected_nic_bonding_right"> + <%= select_with_label "", "ip_address", "type", + [[ "ipv4", "IpV4Address" ], [ "ipv6", "IpV6Address" ] ] %> + </div> + + + <div class="static_ip_common_options"> + <div class="selected_nic_bonding_left">IP Address</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "address" %> + </div> + </div> + + <div id="static_ip_v4_options"> + <div class="selected_nic_bonding_left">Netmask</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "netmask" %> + </div> + + <div class="selected_nic_bonding_left">Broadcast</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "broadcast" %> + </div> + </div> + + <div id="static_ip_v6_options" style="display: none;"> + <div class="selected_nic_bonding_left">Prefix</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "prefix" %> + </div> + </div> + + <div class="static_ip_common_options"> + <div class="selected_nic_bonding_left">Gateway</div> + <div class="selected_nic_bonding_right"> + <%= text_field_with_label "", "ip_address", "gateway" %> + </div> + </div> + +</div> + +<script type="text/javascript"> + $("#ip_address_type").change(function () { + if($("#ip_address_type").val() == 'IpV4Address'){ + $("#static_ip_v4_options").show(); + $("#static_ip_v6_options").hide(); + }else{ + $("#static_ip_v4_options").hide(); + $("#static_ip_v6_options").show(); + } + }).trigger('change'); +</script> diff --git a/src/app/views/network/_ip_addresses_form.rhtml b/src/app/views/network/_ip_addresses_form.rhtml new file mode 100644 index 0000000..f833b2a --- /dev/null +++ b/src/app/views/network/_ip_addresses_form.rhtml @@ -0,0 +1,50 @@ +<div id="select_ip_address" class="popup-content-selection"> +<%= select_with_label "IP Addresses", "ip_address", "id", [] %> +</div> + +<div id="selected_ip_address" class="selected_popup_content"></div> + +<script type="text/javascript"> +function reset_selected_ip_address(){ + var data='Select IP Address<br/>'; + + $("#selected_ip_address").html(data); + $("#ip_addresses_footer").show(); +}; + +reset_selected_ip_address(); // run it once for inital content + +function reset_ip_address_select(){ + // incase of new additions / deletions, repopulate select box + $.getJSON( + "<%= url_for :action => 'ip_addresses_json', :id => parent_id, :parent_type => parent_type %>", + {}, + function(j){ + var options = "<option value=''></option>" + + "<option value='New'>New</option>"; + for(var i = 0; i < j.length; i++){ + options += '<option value="' + j[i].id + '">' + j[i].name + + '</option>'; + } + $("#ip_address_id").html(options); + }); + + $("#ip_address_id option:first").attr("selected", true); +}; + +reset_ip_address_select(); // run it once for initial content + +$("#ip_address_id").change(function () { + if($('#ip_address_id').val() == "New"){ + $("#selected_ip_address").load("<%= url_for :action => 'new_ip_address', :id => parent_id, :parent_type => parent_type %>"); + $("#ip_addresses_footer").hide(); + }else if($('#ip_address_id').val() != ""){ + $("#selected_ip_address").load("<%= url_for :action => 'edit_ip_address'%>/" + $('#ip_address_id').val() + "?parent_type=<%= parent_type %>"); + $("#ip_addresses_footer").hide(); + }else{ + reset_selected_ip_address(); + } +}); + +</script> + diff --git a/src/app/views/network/_select.rhtml b/src/app/views/network/_select.rhtml new file mode 100644 index 0000000..e69d9b0 --- /dev/null +++ b/src/app/views/network/_select.rhtml @@ -0,0 +1,32 @@ +<% target = 'nic' unless target + network_id = 'physical_network_id' if target == 'nic' + network_id = 'vlan_id' if target == 'bonding' + + %> + +<div class="selected_popup_content_left">Network:</div> +<div class="selected_popup_content_right"> + <%= select_with_label "", target, network_id, + @networks.collect { |n| [n.name + ' - ' + n.boot_type.label, n.id ] } %> + +</div> + +<script type="text/javascript"> + var static_network_ids=<%= static_network_ids = '[' + @networks.each { |n| + static_network_ids += ',' if static_network_ids != '[' + static_network_ids += n.id.to_s if n.boot_type_id == @static_boot_type.id + } + static_network_ids += ']' + static_network_ids %>; + + $("#<%=target %>_<%= network_id %>").change(function () { + $("#static_ip_options").hide(); + for(i=0; i<static_network_ids.length; ++i){ + if($("#<%= target %>_<%= network_id %>").val() == static_network_ids[i]){ + $("#static_ip_options").show(); + break; + } + }; + }); +</script> diff --git a/src/app/views/network/edit.rhtml b/src/app/views/network/edit.rhtml new file mode 100644 index 0000000..6360b3f --- /dev/null +++ b/src/app/views/network/edit.rhtml @@ -0,0 +1,34 @@ +<%- content_for :title do -%> + Edit Network +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + + <!-- DIALOG BODY --> + <form method="POST" action="<%= url_for :action => 'update' %>" id="network_form" > + <div class="dialog_form"> + <%= hidden_field_tag 'id', @network.id %> + <%= render :partial => 'form', :locals => { :create => false } %> + </div> + <!-- DIALOG FOOTER --> + <%= popup_footer("$('#network_form').submit()", "Edit Network") %> + </form> + +<script type="text/javascript"> +$(function() { + var networkoptions = { + target: '<%= url_for :action => 'update' %>', + dataType: 'json', + success: function(response, status) { + afterNetwork(response, status); + refresh_summary_static('networks_selection', + '<div class="selection_left"><div>Select a network.</div></div>'); + } + }; + + // bind form using 'ajaxForm' + $('#network_form').ajaxForm(networkoptions); +}); +</script> + + diff --git a/src/app/views/network/edit_bonding.rhtml b/src/app/views/network/edit_bonding.rhtml new file mode 100644 index 0000000..e7f3cda --- /dev/null +++ b/src/app/views/network/edit_bonding.rhtml @@ -0,0 +1,60 @@ +<form method="POST" + action="<%= url_for :action => 'update_bonding' %>" id="edit_bonding_form" > + <div id="selected_popup_content_header"> + Editing Bonded Interface + </div> + + <%= hidden_field_tag('id', @bonding.id) %> + <%= render :partial => 'bonding_form' %> + +</form> + +<form method="POST" action="<%= url_for :action => 'destroy_bonding' %>" id="delete_bonding_form" > + <%= hidden_field_tag('id', @bonding.id) %> +</form> + +<%= multi_button_popup_footer({"Edit Bonding" => + "$('#edit_bonding_form').submit()", + "Delete Bonding" => + "$('#delete_bonding_form').submit()"}) %> + +<script type="text/javascript"> +$(function() { + var edit_bonding_options = { + target: '<%= url_for :action => 'update_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + refresh_summary('hosts_selection', + '<%= url_for :controller => "host", + :action => "show" %>', + <%= @host.id %>) + } + } + }; + + var delete_bonding_options = { + target: '<%= url_for :action => 'destroy_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + refresh_summary('hosts_selection', + '<%= url_for :controller => "host", + :action => "show" %>', + <%= @host.id %>) + } + } + }; + + // bind forms using 'ajaxForm' + $('#edit_bonding_form').ajaxForm(edit_bonding_options); + $('#delete_bonding_form').ajaxForm(delete_bonding_options); + +}); +</script> diff --git a/src/app/views/network/edit_ip_address.rhtml b/src/app/views/network/edit_ip_address.rhtml new file mode 100644 index 0000000..60fe8f1 --- /dev/null +++ b/src/app/views/network/edit_ip_address.rhtml @@ -0,0 +1,76 @@ +<% if @parent_type == 'network' %> +<form method="POST" + action="<%= url_for :action => 'update_ip_address' %>" + id="edit_ip_address_form" > +<% end %> + + <div id="selected_popup_content_header"> + Editing IP Address + </div> + + <% if @parent_type != 'network' %> + <a href="#" onClick="$('#delete_ip_address_form').submit()" style="color: blue;">Delete</a> + <% end %> + + <%= hidden_field_tag('id', @ip_address.id) %> + <%= render :partial => 'ip_address_form' %> + +<% if @parent_type == 'network' %> +</form> +<% end %> + +<form method="POST" + action="<%= url_for :action => 'destroy_ip_address' %>" + id="delete_ip_address_form" > + <%= hidden_field_tag('id', @ip_address.id) %> +</form> + +<% if @parent_type == 'network' %> +<%= multi_button_popup_footer({" Edit IP Address" => + "$('#edit_ip_address_form').submit()", + "Delete IP Address" => + "$('#delete_ip_address_form').submit()"}) %> +<% end %> + +<script type="text/javascript"> +$(function() { + var edit_ip_address_options = { + target: '<%= url_for :action => 'update_ip_address' %>', // target element to update + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + <% if @parent_type == 'network' %> + refresh_summary('networks_selection', + '<%= url_for :controller => "network", :action => "show" %>', + <%= @network.id %>); + <% end %> + } + } + }; + + var delete_ip_address_options = { + target: '<%= url_for :action => 'delete_ip_address' %>', // target element to update + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + <% if @parent_type == 'network' %> + refresh_summary('networks_selection', + '<%= url_for :controller => "network", :action => "show" %>', + <%= @network.id %>); + <% end %> + } + } + }; + + // bind forms using 'ajaxForm' + $('#edit_ip_address_form').ajaxForm(edit_ip_address_options); + $('#delete_ip_address_form').ajaxForm(delete_ip_address_options); + +}); +</script> diff --git a/src/app/views/network/edit_network_ip_addresses.rhtml b/src/app/views/network/edit_network_ip_addresses.rhtml new file mode 100644 index 0000000..7a1e4cb --- /dev/null +++ b/src/app/views/network/edit_network_ip_addresses.rhtml @@ -0,0 +1,13 @@ +<%- content_for :title do -%> + Edit Network IP Addresses +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + +<%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'network', + :parent_id => @network.id } %> + +<div id="ip_addresses_footer" class="popup-content-footer"> + <%= ok_footer %> +</div> diff --git a/src/app/views/network/edit_nic.rhtml b/src/app/views/network/edit_nic.rhtml new file mode 100644 index 0000000..75af6fb --- /dev/null +++ b/src/app/views/network/edit_nic.rhtml @@ -0,0 +1,62 @@ +<form method="POST" + action="<%= url_for :action => 'update_nic' %>" id="nic_form" > + <div id="selected_popup_content_header"> + Editing NIC + </div> + + <%= error_messages_for 'nic' %> + + <div id="selected_popup_content_expanded" class="dialog_form"> + <%= hidden_field_tag 'id', @nic.id %> + <%= hidden_field_tag 'nic_host_id', @nic.host.id %> + <%= hidden_field_tag 'nic_network_id', @nic.physical_network.id %> + + <div class="selected_popup_content_left">MAC:</div> + <div class="selected_popup_content_right"><%= @nic.mac %></div> + + <% if @nic.host.bondings.size != 0 %> + <div class="selected_popup_content_left">Bonded Interfaces</div> + <div class="selected_popup_content_right"> + <select id="nic_bonding_ids" name="nic[bonding_ids][]"> + <option value="" /> + <%= options_from_collection_for_select @nic.host.bondings, + "id", "name", + @nic.bondings.size > 0 ? @nic.bondings[0].bonding_id.to_i : "" %> + </select> + </div> + <% end %> + + + <%= render :partial => 'select' %> + + <div id="static_ip_options" + style="<% if @network.boot_type_id != @static_boot_type.id %> + display: none;<%end %>"> + <%= render :partial => 'ip_addresses_form', + :locals => { :parent_type => 'nic', + :parent_id => @nic.id } %> + </div> + + + </div> + <%= popup_footer("$('#nic_form').submit()", "Edit NIC") %> +</form> + +<script type="text/javascript"> +$(function() { + var nicoptions = { + target: '<%= url_for :action => 'update_nic' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_nics_select(); + reset_nics_bonding_detail(); + } + } + }; + + // bind form using 'ajaxForm' + $('#nic_form').ajaxForm(nicoptions); +}); +</script> diff --git a/src/app/views/network/list.html.erb b/src/app/views/network/list.html.erb new file mode 100644 index 0000000..9a304cf --- /dev/null +++ b/src/app/views/network/list.html.erb @@ -0,0 +1,72 @@ +<div id="toolbar_nav"> +<ul> + <li><a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]"><%= image_tag "icon_addstorage.png", :style => "vertical-align:middle;" %> Add Network</a></li> + <li> + <a href="#" onClick="delete_networks();" > + <%= image_tag "icon_remove.png", :style=>"vertical-align:middle;" %> + Remove + </a> + </li> + </ul> +</div> + +<script type="text/javascript"> + function delete_networks(){ + var networks = get_selected_networks(); + if (validate_selected(get_selected_networks(), 'networks')) { + $.post('<%= url_for :action => 'delete' %>', + { network_ids: networks.toString() }, + function(data,status){ + if (data.alert) { + $.jGrowl(data.alert); + } + grid = $("#networks_grid"); + if (grid.size()>0 && grid != null) { + grid.flexReload(); + } + empty_summary('networks_selection', 'Network'); + }, 'json'); + } + } + + function networks_select(selected_rows) + { + var selected_ids = new Array(); + for(i=0; i<selected_rows.length; i++) { + selected_ids[i] = selected_rows[i].id; + } + if (selected_ids.length == 1) + { + $('#networks_selection').load('<%= url_for :action => "show" %>', + { id: parseInt(selected_ids[0].substring(3))}); + } + } + +</script> + +<div class="panel_header"></div> +<% if @networks.size != 0 %> + <div class="data_section"> + <%= render :partial => "grid", :locals => { :table_id => "networks_grid", + :networks => @networks, + :on_select => "networks_select"} %> + </div> + + <div class="selection_detail" id="networks_selection"> + <div class="selection_left"> + <div>Select a network.</div> + </div> + </div> +<% else %> + <div class="data_section"> + <div class="no-grid-items"> + <%= image_tag 'no-grid-items.png', :style => 'float: left;' %> + + <div class="no-grid-items-text"> + No networks found. <br/><br/> + <%= image_tag "icon_addhost.png", :style=>"vertical-align:middle;" %> + <a href="<%= url_for :action => 'new' %>" rel="facebox[.bolder]">Add first network</a> + </div> + </div> + </div> +<% end %> diff --git a/src/app/views/network/new.rhtml b/src/app/views/network/new.rhtml new file mode 100644 index 0000000..15b7304 --- /dev/null +++ b/src/app/views/network/new.rhtml @@ -0,0 +1,28 @@ +<%- content_for :title do -%> + Add Network +<%- end -%> +<%- content_for :description do -%> +<%- end -%> + + <!-- DIALOG BODY --> + <form method="POST" action="<%= url_for :action => 'create' %>" id="network_form" > + <div class="dialog_form"> + <%= render :partial => 'form', :locals => { :create => true } %> + </div> + <!-- DIALOG FOOTER --> + <%= popup_footer("$('#network_form').submit()", "Add Network") %> + </form> + +<script type="text/javascript"> +$(function() { + var networkoptions = { + target: '<%= url_for :action => 'create' %>', + dataType: 'json', + success: afterNetwork // post-submit callback + }; + + // bind form using 'ajaxForm' + $('#network_form').ajaxForm(networkoptions); +}); +</script> + diff --git a/src/app/views/network/new_bonding.rhtml b/src/app/views/network/new_bonding.rhtml new file mode 100644 index 0000000..76d3b94 --- /dev/null +++ b/src/app/views/network/new_bonding.rhtml @@ -0,0 +1,36 @@ +<form method="POST" + action="<%= url_for :action => 'create_bonding' %>" id="bonding_form" > + + <div id="selected_popup_content_header"> + Create Bonded Interface + </div> + + <%= render :partial => 'bonding_form' %> +</form> + +<%= multi_button_popup_footer({"Create Bonding" => + "$('#bonding_form').submit()"}) %> + +<script type="text/javascript"> +$(function() { + var bonding_options = { + target: '<%= url_for :action => 'create_bonding' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_bonding_select(); + reset_nics_bonding_detail(); + refresh_summary('hosts_selection', + '<%= url_for :controller => "host", + :action => "show" %>', + <%= @host.id %>) + } + } + }; + + // bind forms using 'ajaxForm' + $('#bonding_form').ajaxForm(bonding_options); + +}); +</script> diff --git a/src/app/views/network/new_ip_address.rhtml b/src/app/views/network/new_ip_address.rhtml new file mode 100644 index 0000000..df2a2bb --- /dev/null +++ b/src/app/views/network/new_ip_address.rhtml @@ -0,0 +1,44 @@ +<% if @parent_type == 'network' %> +<form method="POST" + action="<%= url_for :action => 'create_ip_address' %>" + id="ip_address_form" > +<% end %> + + <div id="selected_popup_content_header"> + Create Ip Address + </div> + + <%= render :partial => 'ip_address_form' %> + +<% if @parent_type == 'network' %> +</form> +<% end %> + +<% if @parent_type == 'network' %> +<%= multi_button_popup_footer({"Create IP Address" => + "$('#ip_address_form').submit()"}) %> +<% end %> + +<script type="text/javascript"> +$(function() { + var ip_address_options = { + target: '<%= url_for :action => 'create_ip_address' %>', + dataType: 'json', + success: function(response, status) { + ajax_validation(response, status); + if (response.success) { + reset_selected_ip_address(); + reset_ip_address_select(); + <% if @parent_type == 'network' %> + refresh_summary('networks_selection', + '<%= url_for :controller => "network", :action => "show" %>', + <%= @network.id %>); + <% end %> + } + } + }; + + // bind forms using 'ajaxForm' + $('#ip_address_form').ajaxForm(ip_address_options); +}); +</script> diff --git a/src/app/views/network/show.rhtml b/src/app/views/network/show.rhtml new file mode 100644 index 0000000..2ac68ea --- /dev/null +++ b/src/app/views/network/show.rhtml @@ -0,0 +1,31 @@ +<%- content_for :title do -%> + <%=h @network.name %> +<%- end -%> + +<%- content_for :action_links do -%> + <%= link_to image_tag("icon_edit.png") + "Edit", + {:action => 'edit', :id => @network.id }, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> + + <%= link_to image_tag("icon_edit.png") + "Edit IP Addresses", + {:action => 'edit_network_ip_addresses', :id => @network.id }, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> +<%- end -%> + +<div id="hosts_selection_id" style="display:none"><%= @host.id %></div> +<div class="selection_key"> + Name:<br/> + Type:<br/> + Boot Type:<br/> + IP Addresses:<br/> +</div> +<div class="selection_value"> + <%=h @network.name %><br/> + <%=h @network.type %><br/> + <%=h @network.boot_type.label %><br/> + <%=@network.ip_addresses.collect{ |ip| + ip.address}.join("<br/>") %> +</div> + +<%- content_for :right do -%> +<%- end -%> diff --git a/src/app/views/nic/_list.rhtml b/src/app/views/nic/_list.rhtml index 07f7e44..eb3a4a7 100644 --- a/src/app/views/nic/_list.rhtml +++ b/src/app/views/nic/_list.rhtml @@ -1,7 +1,6 @@ <table class='listing'> <thead> <tr> - <th class="empty">IP</th> <th>MAC</th> <th>Bridge</th> <th>Usage Type</th> @@ -11,7 +10,6 @@ </thead> <% for nic in nics %> <tr class="<%= cycle('odd','even', :name => nics) %>"> - <td><%= link_to nic.ip_addr, { :controller => "nic", :action => 'show', :id => nic }, { :class => "show" } %></td> <td><%= nic.mac %></td> <td><%= nic.bridge %></td> <td><%= nic.usage_type %></td> diff --git a/src/db/migrate/028_refactor_networking_model.rb b/src/db/migrate/028_refactor_networking_model.rb new file mode 100644 index 0000000..946b976 --- /dev/null +++ b/src/db/migrate/028_refactor_networking_model.rb @@ -0,0 +1,271 @@ +# Copyright (C) 2008 Red Hat, Inc. +# Written by Mohammed Morsi <mmorsi at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# introduce networks and ip_addresses tables, refactor relationships +class RefactorNetworkingModel < ActiveRecord::Migration + def self.up + + #################################################### + # bugfix, bridge tables shouldn't have their own ids + remove_column :bondings_nics, :id + + ################################################################## + # add networks, usages tables and networks_usage_types bridge + create_table :networks do |t| + t.string :type, :null => false + t.string :name, :null => false + t.integer :boot_type_id, :null => false + + # attributes for Vlan (type=Vlan) + t.integer :number + end + + create_table :usages do |t| + t.string :label, :null => false + t.string :usage, :null => false + end + + create_table :networks_usages, :id => false do |t| + t.integer :network_id, :null => false + t.integer :usage_id, :null => false + end + + add_index :networks_usages, [:network_id, :usage_id], :unique => true + + # create usages + Usage.create(:label => 'Guest', :usage => 'guest') + Usage.create(:label => 'Management', :usage => 'management') + Usage.create(:label => 'Storage', :usage => 'storage') + + # referential integrity for networks tables + execute "alter table networks add constraint + fk_network_boot_types + foreign key (boot_type_id) references + boot_types(id)" + execute "alter table networks_usages add constraint + fk_networks_usages_network_id + foreign key (network_id) references + networks(id)" + execute "alter table networks_usages add constraint + fk_networks_usages_usage_id + foreign key (usage_id) references + usages(id)" + + # add foreign keys to nics / bondings table + add_column :nics, :physical_network_id, :integer + add_column :bondings, :vlan_id, :integer + + # referential integrity for nic/bondings network ids + execute "alter table nics add constraint + fk_nic_networks + foreign key (physical_network_id) references + networks(id)" + execute "alter table bondings add constraint + fk_bonding_networks + foreign key (vlan_id) references + networks(id)" + + #################################################### + # create ip_addresses table + create_table :ip_addresses do |t| + t.string :type + + # foreign keys to associated entities + t.integer :nic_id + t.integer :bonding_id + t.integer :network_id + + # common attributes + t.string :address, :limit => 39, :null => false + t.string :gateway, :limit => 39 + + # attributes for IPv4 (type=IpV4Address) + t.string :netmask, :limit => 15 + t.string :broadcast, :limit => 15 + + # attributes for IPv6 (type=IpV6Address) + t.string :prefix, :limit => 39 + t.timestamps + end + + # referential integrity for ip_addresses table + execute "alter table ip_addresses add constraint + fk_nic_ip_addresses + foreign key (nic_id) references nics(id)" + execute "alter table ip_addresses add constraint + fk_bonding_ip_addresses + foreign key (bonding_id) references bondings(id)" + execute "alter table ip_addresses add constraint + fk_network_ip_addresses + foreign key (network_id) references networks(id)" + + ################################################################### + static_boot_type_id + BootType.find(:first, + :conditions => {:proto => 'static'} ).id + + # migrate nic ip_addresses to networks / ip_addresses table + i = 0 + Nic.find(:all).each do |nic| + if nic.boot_type_id == static_boot_type_id + IpV4Address.new(:nic_id => nic.id, + :address => nic.ip_addr).save! + + end + network = PhysicalNetwork.new( + :name => 'Physical Network ' + i.to_s, + :boot_type_id => nic.boot_type_id) + network.save! + + ip_address = IpV4Address.new(:address => nic.ip_addr ? nic.ip_addr : '0.0.0.0', + :netmask => nic.netmask, + :broadcast => nic.broadcast, + :gateway => nic.ip_addr) + ip_address.network = network + ip_address.save! + + nic.physical_network = network + nic.save! + + i += 1 + end + + # migrate bonding ip_addresses to networks / ip_addresses table + i = 0 + Bonding.find(:all).each do |bonding| + if bonding.boot_type_id == static_boot_type_id + IpV4Address.new(:bonding_id => bonding.id, + :address => bonding.ip_addr).save! + end + network = Vlan.new( + :name => 'VLAN ' + i.to_s, + :number => i, + :boot_type_id => bonding.boot_type_id) + network.save! + + ip_address = IpV4Address.new(:address => bonding.ip_addr ? bonding.ip_addr : '0.0.0.0', + :netmask => bonding.netmask, + :broadcast => bonding.broadcast, + :gateway => bonding.ip_addr) + ip_address.network = network + ip_address.save! + + bonding.vlan = network + bonding.save! + + i += 1 + end + + ############################################################## + # remove nics / bonding ip address and network related columns + remove_column :nics, :ip_addr + remove_column :nics, :netmask + remove_column :nics, :broadcast + remove_column :nics, :boot_type_id + remove_column :bondings, :ip_addr + remove_column :bondings, :netmask + remove_column :bondings, :broadcast + remove_column :bondings, :boot_type_id + + + end + + def self.down + ############################################################## + # readd nics / bonding ip address related columns + add_column :nics, :ip_addr, :string, :limit => 16 + add_column :nics, :netmask, :string, :limit => 16 + add_column :nics, :broadcast, :string, :limit => 16 + add_column :nics, :boot_type_id, :integer + add_column :bondings, :ip_addr, :string, :limit => 16 + add_column :bondings, :netmask, :string, :limit => 16 + add_column :bondings, :broadcast, :string, :limit => 16 + add_column :bondings, :boot_type_id, :integer + + execute "alter table nics add constraint + fk_nic_boot_types + foreign key (boot_type_id) references + boot_types(id)" + execute "alter table bondings add constraint + fk_bonding_boot_types + foreign key (boot_type_id) references + boot_types(id)" + + ############################################################## + # attempt to migrate ip information back into nics table. + # because a nic can have multiple ips (if statically + # assigned) as well as its network, just use the 1st + # found + Nic.find(:all).each do |nic| + if nic.physical_network.ip_addresses.size > 0 + # use the 1st configured network ip + nic.ip_addr = nic.physical_network.ip_addresses[0].address + nic.netmask = nic.physical_network.ip_addresses[0].netmask + nic.broadcast = nic.physical_network.ip_addresses[0].broadcast + end + + if nic.ip_addresses.size > 0 + # use the 1st assigned static ip + nic.ip_addr = nic.ip_addresses[0].address + end + + nic.boot_type_id = nic.physical_network.boot_type_id + + nic.save! + end + + # attempt to migrate ip information back into bondings table. + # because a bonding can have multiple ips (if statically + # assigned) as well as its network, just use the 1st + # found + Bonding.find(:all).each do |bonding| + if bonding.vlan.ip_addresses.size > 0 + # use the 1st configured network ip + bonding.ip_addr = bonding.vlan.ip_addresses[0].address + bonding.netmask = bonding.vlan.ip_addresses[0].netmask + bonding.broadcast = bonding.vlan.ip_addresses[0].broadcast + end + + if bonding.ip_addresses.size > 0 + # use the 1st assigned static ip + bonding.ip_addr = bonding.ip_addresses[0].address + end + + bonding.boot_type_id = bonding.vlan.boot_type_id + + bonding.save! + end + + ############################################################## + # drop ip_addresses table + drop_table :ip_addresses + + # drop network ids from nics / bondings table + remove_column :nics, :physical_network_id + remove_column :bondings, :vlan_id + + # drop networks tables + drop_table :networks_usages + drop_table :usages + drop_table :networks + + ############################################################## + # undo bugfix above + add_column :bondings_nics, :id, :integer + end +end diff --git a/src/public/javascripts/ovirt.js b/src/public/javascripts/ovirt.js index 4579c80..d84b3d9 100644 --- a/src/public/javascripts/ovirt.js +++ b/src/public/javascripts/ovirt.js @@ -297,4 +297,23 @@ function delete_pool(delete_url, id) $.jGrowl(data.alert); } }, 'json'); -} \ No newline at end of file +} + + +function get_selected_networks() +{ + return get_selected_checkboxes("networks_grid_form"); +} + +function afterNetwork(response, status){ + ajax_validation(response, status); + if (response.success) { + $(document).trigger('close.facebox'); + grid = $("#networks_grid"); + if (grid.size()>0 && grid != null) { + grid.flexReload(); + } else { + $tabs.tabs("load",$tabs.data('selected.tabs')); + } + } +} diff --git a/src/public/stylesheets/components.css b/src/public/stylesheets/components.css index 16eaf62..228ff7b 100644 --- a/src/public/stylesheets/components.css +++ b/src/public/stylesheets/components.css @@ -267,4 +267,52 @@ .detail-pane-chart { height: 50px; width: 375px; -}; +} + + +/************************* + * new popup components + *************************/ +.popup-content-selection { + float: left; + width: 45%; + padding-left: 20px; + padding-top: 10px; +} + +.popup-content-selection select{ + min-width: 200px; +} + +.popup-content-footer{ + width: 99%; + float: left; +} + +.selected_popup_content { + padding-left: 20px; + min-height: 50px; + float: left; + width: 96%; +} + +#selected_popup_content_header { + padding-bottom: 5px; + font-weight: bold; +} + +#selected_popup_content_expanded{ + padding-bottom: 50px; +} + +.selected_popup_content_left { + float: left; + width: 40%; + padding-bottom: 15px; +} + +.selected_popup_content_right { + float: left; + width: 40%; + padding-bottom: 15px; +} -- 1.5.6.5