[Ovirt-devel] [PATCH server] network integration into ovirt server db and wui

Darryl Pierce dpierce at redhat.com
Fri Oct 31 14:06:56 UTC 2008


-----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_id

This 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")
> +    end

In 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")
> +    end

See 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_id

These 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 and

You 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_id

Use 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_id

Use 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>


More information about the ovirt-devel mailing list