[Ovirt-devel] [PATCH server] add option to forward node's / vm's vnc port in server/taskomatic

Mohammed Morsi mmorsi at redhat.com
Thu Jan 15 20:46:18 UTC 2009


this patch adds a 'Forward vnc port locally' checkbox and 'port'
text box to the vm create / edit form. Fields are added to the
database to support the user's selection and taskomatic is updated
to configure the local firewall accordingly.

this patch has been tested up to setting the fields via the wui
and using these fields to correctly configure the firewall via
taskomatic. testing connecting to the vm via vnc/virt/ovirt-viewer
hasn't is in progress and thus this may need some additional work.
---
 src/app/controllers/vm_controller.rb |    2 +
 src/app/models/vm.rb                 |    4 +
 src/app/views/vm/_form.rhtml         |   14 ++++
 src/app/views/vm/show.rhtml          |    4 +
 src/db/migrate/034_add_vm_vnc.rb     |   30 +++++++++
 src/task-omatic/task_vm.rb           |    6 ++-
 src/task-omatic/vnc.rb               |  109 ++++++++++++++++++++++++++++++++++
 7 files changed, 168 insertions(+), 1 deletions(-)
 create mode 100644 src/db/migrate/034_add_vm_vnc.rb
 create mode 100644 src/task-omatic/vnc.rb

diff --git a/src/app/controllers/vm_controller.rb b/src/app/controllers/vm_controller.rb
index 701dea8..8622b89 100644
--- a/src/app/controllers/vm_controller.rb
+++ b/src/app/controllers/vm_controller.rb
@@ -116,6 +116,7 @@ class VmController < ApplicationController
         new_storage_ids = new_storage_ids.sort.collect {|x| x.to_i }
         needs_restart = true unless current_storage_ids == new_storage_ids
       end
+      params[:vm][:forward_vnc] = params[:forward_vnc]
       params[:vm][:needs_restart] = 1 if needs_restart
       @vm.update_attributes!(params[:vm])
       _setup_vm_provision(params)
@@ -346,6 +347,7 @@ class VmController < ApplicationController
       vm_resource_pool.create_with_parent(hardware_pool)
       params[:vm][:vm_resource_pool_id] = vm_resource_pool.id
     end
+    params[:vm][:forward_vnc] = params[:forward_vnc]
     @vm = Vm.new(params[:vm])
     @perm_obj = @vm.vm_resource_pool
     @redir_controller = 'resources'
diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb
index 227f343..cbc5ec0 100644
--- a/src/app/models/vm.rb
+++ b/src/app/models/vm.rb
@@ -36,6 +36,10 @@ class Vm < ActiveRecord::Base
                         :boot_device, :memory_allocated_in_mb,
                         :memory_allocated, :vnic_mac_addr
 
+  validates_numericality_of :forward_vnc_port,
+     :greater_than => 0,
+     :if => Proc.new { |vm| vm.forward_vnc }
+
   acts_as_xapian :texts => [ :uuid, :description, :vnic_mac_addr, :state ],
                  :terms => [ [ :search_users, 'U', "search_users" ] ],
                  :eager_load => :smart_pools
diff --git a/src/app/views/vm/_form.rhtml b/src/app/views/vm/_form.rhtml
index 523e81e..ffc9dd7 100644
--- a/src/app/views/vm/_form.rhtml
+++ b/src/app/views/vm/_form.rhtml
@@ -51,6 +51,20 @@
     <div class="clear_row"></div>
     <div class="clear_row"></div>
 
+    <div style="width: 50%; float: left;">
+    <%= check_box_tag_with_label "Forward vm's vnc <b>port</b> locally", "forward_vnc", 1, @vm.forward_vnc %>
+    </div>
+    <div style="width: 40%; float: left;">
+    <%= text_field_with_label "", "vm", "forward_vnc_port", { :style=>"width: 80px;", :size => 7, :disabled => ! @vm.forward_vnc } %>
+    </div>
+    <div style="clear:both;"></div>
+    <div class="clear_row"></div>
+    <script type="text/javascript">
+      $("#forward_vnc").click(function(){
+        $("#vm_forward_vnc_port").attr("disabled", $("#forward_vnc").is(":checked") ? "" : "disabled");
+      });
+    </script>
+
    <%= check_box_tag_with_label "Start VM Now? (pending current resource availability)", "start_now", nil if create or @vm.state == Vm::STATE_STOPPED %>
    <%= check_box_tag_with_label "Restart VM Now? (pending current resource availability)", "restart_now", nil if @vm.state == Vm::STATE_RUNNING %>
 
diff --git a/src/app/views/vm/show.rhtml b/src/app/views/vm/show.rhtml
index f361131..add29b4 100644
--- a/src/app/views/vm/show.rhtml
+++ b/src/app/views/vm/show.rhtml
@@ -88,6 +88,7 @@
     <div id="vms_selection_id" style="display:none"><%= @vm.id %></div>
     <div class="selection_key">
         Uuid:<br/>
+        <%= @vm.forward_vnc ? "VNC uri:<br/>" : "" %>
 	Num vcpus allocated:<br/>
 	Num vcpus used:<br/>
 	Memory allocated:<br/>
@@ -100,6 +101,9 @@
     </div>
     <div class="selection_value">
        <%=h @vm.uuid %><br/>
+       <%= url = request.url
+           url = request.url[0..(url.index('/', 8) - 1)] + ":" + @vm.forward_vnc_port.to_s
+           @vm.forward_vnc ? (url + "<br/>") : "" %>
        <%=h @vm.num_vcpus_allocated %><br/>
        <%=h @vm.num_vcpus_used %><br/>
        <%=h @vm.memory_allocated_in_mb %> MB<br/>
diff --git a/src/db/migrate/034_add_vm_vnc.rb b/src/db/migrate/034_add_vm_vnc.rb
new file mode 100644
index 0000000..a93e457
--- /dev/null
+++ b/src/db/migrate/034_add_vm_vnc.rb
@@ -0,0 +1,30 @@
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Mohammed Morsi
+#
+# 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 AddVmVnc < ActiveRecord::Migration
+  def self.up
+   add_column :vms, :forward_vnc, :bool, :default => false
+   add_column :vms, :forward_vnc_port, :int, :default => 0
+  end
+
+  def self.down
+   drop_column :vms, :forward_vnc
+   drop_column :vms, :forward_vnc_port
+  end
+end
+
diff --git a/src/task-omatic/task_vm.rb b/src/task-omatic/task_vm.rb
index c187287..8481dd0 100644
--- a/src/task-omatic/task_vm.rb
+++ b/src/task-omatic/task_vm.rb
@@ -20,6 +20,7 @@ require 'rexml/document'
 include REXML
 
 require 'utils'
+require 'vnc'
 
 gem 'cobbler'
 require 'cobbler'
@@ -286,6 +287,8 @@ def shut_or_destroy_vm(task, which)
   vm_orig_state = vm.state
   setVmState(vm, Vm::STATE_STOPPING)
 
+  closeVmVncPort(vm)
+
   begin
     conn = Libvirt::open("qemu+tcp://" + vm.host.hostname + "/system")
     dom = conn.lookup_domain_by_uuid(vm.uuid)
@@ -428,7 +431,8 @@ def start_vm(task)
         dom.create
 
         setVmVncPort(vm, dom)
-      rescue
+        forwardVmVncPort(vm)
+      rescue Exception => ex
         if dom != nil
           dom.undefine
         end
diff --git a/src/task-omatic/vnc.rb b/src/task-omatic/vnc.rb
new file mode 100644
index 0000000..d78485a
--- /dev/null
+++ b/src/task-omatic/vnc.rb
@@ -0,0 +1,109 @@
+# 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.
+
+# FIXME no ruby/libiptc wrapper exists, when
+# it does replace iptables command w/ calls to it
+ at iptables_cmd='/sbin/iptables '
+
+# FIXME replace this w/ dnsruby inclusion / call
+ at dns_lookup_cmd='/usr/bin/dig'
+
+ at vnc_debug = false
+
+############################## 'private' methods
+
+def _debug(msg)
+  puts "\n" + msg + "\n" if @vnc_debug
+end
+
+def _findVmHostIp(vm)
+  cmdout='/tmp/ovirtvnc' + vm.forward_vnc_port.to_s
+  cmd=@dns_lookup_cmd + ' ' + vm.host.hostname + 
+      ' +noall +answer +short > ' + cmdout
+
+  system(cmd)
+
+  result = File.read(cmdout).rstrip
+  _debug( "vm host hostname  resolved to " + result.to_s )
+  return result
+end
+
+def _vncPortOpen?(port)
+  cmdout='/tmp/ovirtvnc' + port.to_s
+  cmd=@iptables_cmd + ' -t nat -nL | grep ' + port.to_s  +
+       ' > ' + cmdout
+  _debug("vncPortOpen? iptables command: " + cmd +
+         " cmdout " + cmdout)
+
+  system(cmd)
+  
+   return File.size(cmdout) != 0
+end
+
+def _natRoutingFilter(vm)
+  return " -p tcp --dport " + vm.forward_vnc_port.to_s + 
+         " -j DNAT --to " + _findVmHostIp(vm) + ":" + vm.forward_vnc_port.to_s + " "
+end
+
+def _masqRoutingFilter(vm)
+  return " -d " + _findVmHostIp(vm) + " -j MASQUERADE "
+end
+
+############################## 'public' methods
+
+
+def forwardVmVncPort(vm)
+  return unless vm.forward_vnc
+  unless vm.forward_vnc_port > 0 
+    raise "Must specify valid port to forward " + vm.forward_vnc_port.to_s
+  end
+  
+   if _vncPortOpen?(vm.forward_vnc_port)
+     raise "Port already open " + vm.forward_vnc_port.to_s
+   end
+
+   nat_rule = @iptables_cmd + " -t nat -A PREROUTING " + 
+              _natRoutingFilter(vm)
+   masq_rule = @iptables_cmd + " -t nat -A POSTROUTING " +
+              _masqRoutingFilter(vm)
+   _debug("forwardVmVncPort nat_rule: " + nat_rule + 
+          " masq_rule: " + masq_rule)
+   system(nat_rule)
+   system(masq_rule)
+end
+
+def closeVmVncPort(vm)
+  # FIXME forward_vnc may have been changed while the vm is running
+  return unless vm.forward_vnc
+  unless vm.forward_vnc_port > 0 
+    raise "Must specify valid port to forward " + vm.forward_vnc_port.to_s
+  end
+
+   unless _vncPortOpen?(vm.forward_vnc_port)
+     raise "Port not open " + vm.forward_vnc_port.to_s
+   end
+
+   nat_rule = @iptables_cmd + " -t nat -D PREROUTING " + 
+              _natRoutingFilter(vm)
+   masq_rule = @iptables_cmd + " -t nat -D POSTROUTING " +
+              _masqRoutingFilter(vm)
+   _debug("closeVmVncPort nat_rule: " + nat_rule + 
+          " masq_rule: " + masq_rule)
+   system(nat_rule)
+   system(masq_rule)
+end
-- 
1.6.0.6




More information about the ovirt-devel mailing list