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

Mohammed Morsi mmorsi at redhat.com
Fri Oct 31 21:14:27 UTC 2008


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




More information about the ovirt-devel mailing list