[Ovirt-devel] [PATCH server] allow admin to setup iptables port forwarding on server for a vm's vnc port
Mohammed Morsi
mmorsi at redhat.com
Tue Jan 27 22:32:01 UTC 2009
---
src/app/controllers/vm_controller.rb | 2 +
src/app/models/vm.rb | 4 +
src/app/views/vm/_form.rhtml | 15 ++++
src/app/views/vm/show.rhtml | 4 +
src/db/migrate/034_add_vm_vnc.rb | 30 +++++++
src/task-omatic/taskomatic.rb | 4 +
src/task-omatic/vnc.rb | 140 ++++++++++++++++++++++++++++++++++
7 files changed, 199 insertions(+), 0 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 56501fd..8e1cbee 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)
@@ -345,6 +346,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
@current_pool_id=@perm_obj.id
diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb
index bf99e2d..15af463 100644
--- a/src/app/models/vm.rb
+++ b/src/app/models/vm.rb
@@ -40,6 +40,10 @@ class Vm < ActiveRecord::Base
validates_format_of :uuid,
:with => %r([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})
+ validates_numericality_of :forward_vnc_port,
+ :greater_than => 0,
+ :if => Proc.new { |vm| vm.forward_vnc }
+
validates_numericality_of :needs_restart,
:greater_than_or_equal_to => 0,
:less_than_or_equal_to => 1,
diff --git a/src/app/views/vm/_form.rhtml b/src/app/views/vm/_form.rhtml
index 523e81e..486798f 100644
--- a/src/app/views/vm/_form.rhtml
+++ b/src/app/views/vm/_form.rhtml
@@ -51,6 +51,21 @@
<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/taskomatic.rb b/src/task-omatic/taskomatic.rb
index bcb9bd3..dca6fb7 100755
--- a/src/task-omatic/taskomatic.rb
+++ b/src/task-omatic/taskomatic.rb
@@ -32,6 +32,7 @@ include Daemonize
require 'task_vm'
require 'task_storage'
+require 'vnc'
class TaskOmatic
@@ -232,6 +233,8 @@ class TaskOmatic
raise "Error destroying VM: #{result.text}" unless result.status == 0
end
+ closeVmVncPort(vm)
+
# undefine can fail, for instance, if we live migrated from A -> B, and
# then we are shutting down the VM on B (because it only has "transient"
# XML). Therefore, just ignore undefine errors so we do the rest
@@ -303,6 +306,7 @@ class TaskOmatic
# of places so you'll see a lot of .reloads.
db_vm.reload
set_vm_vnc_port(db_vm, result.description) unless result.status != 0
+ forwardVmVncPort(db_vm)
# This information is not available via the libvirt interface.
db_vm.memory_used = db_vm.memory_allocated
diff --git a/src/task-omatic/vnc.rb b/src/task-omatic/vnc.rb
new file mode 100644
index 0000000..3eb0ca6
--- /dev/null
+++ b/src/task-omatic/vnc.rb
@@ -0,0 +1,140 @@
+# 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.
+
+# TODO no ruby/libiptc wrapper exists, when
+# it does replace iptables command w/ calls to it
+ at iptables_cmd='/sbin/iptables '
+
+# TODO replace this w/ dnsruby inclusion / call
+ at dns_lookup_cmd='/usr/bin/dig'
+
+ at ip_forward_command='echo 1 > /proc/sys/net/ipv4/ip_forward'
+
+ at vnc_debug = false
+
+# TODO can this be retreived in any way
+# since machine will have both external
+# and internal network interface
+ at local_ip = '192.168.50.2'
+
+############################## '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 _forwardRules(vm)
+ ip = _findVmHostIp(vm)
+ return " -d " + ip + " -p tcp --dport " + vm.vnc_port.to_s + " -j ACCEPT",
+ " -s " + ip + " -p tcp --sport " + vm.vnc_port.to_s + " -j ACCEPT"
+end
+
+def _natRules(vm)
+ ip = _findVmHostIp(vm)
+
+ # TODO should a "-d external_server_ip" be added to DNAT?
+ return " -p tcp --dport " + vm.forward_vnc_port.to_s + " -j DNAT --to " + ip + ":" + vm.vnc_port.to_s,
+ " -d " + ip + " -p tcp --dport " + vm.vnc_port.to_s + " -j SNAT --to " + @local_ip
+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
+
+ forward_rule1, forward_rule2 = _forwardRules(vm)
+ forward_rule1 = @iptables_cmd + " -A FORWARD " + forward_rule1
+ forward_rule2 = @iptables_cmd + " -A FORWARD " + forward_rule2
+
+ prerouting_rule, postrouting_rule = _natRules(vm)
+ prerouting_rule = @iptables_cmd + " -t nat -A PREROUTING " + prerouting_rule
+ postrouting_rule = @iptables_cmd + " -t nat -A POSTROUTING " + postrouting_rule
+
+ _debug(" open\n forward rule 1: " + forward_rule1 +
+ "\n forward_rule 2: " + forward_rule2 +
+ "\n prerouting rule: " + prerouting_rule +
+ "\n postrouting rule: " + postrouting_rule)
+
+ system(forward_rule1)
+ system(forward_rule2)
+ system(prerouting_rule)
+ system(postrouting_rule)
+ system(@ip_forward_command)
+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
+
+ forward_rule1, forward_rule2 = _forwardRules(vm)
+ forward_rule1 = @iptables_cmd + " -D FORWARD " + forward_rule1
+ forward_rule2 = @iptables_cmd + " -D FORWARD " + forward_rule2
+
+ prerouting_rule, postrouting_rule = _natRules(vm)
+ prerouting_rule = @iptables_cmd + " -t nat -D PREROUTING " + prerouting_rule
+ postrouting_rule = @iptables_cmd + " -t nat -D POSTROUTING " + postrouting_rule
+
+ _debug(" close\n forward rule 1: " + forward_rule1 +
+ "\n forward_rule 2: " + forward_rule2 +
+ "\n prerouting rule: " + prerouting_rule +
+ "\n postrouting rule: " + postrouting_rule)
+
+ system(forward_rule1)
+ system(forward_rule2)
+ system(prerouting_rule)
+ system(postrouting_rule)
+end
--
1.6.0.6
More information about the ovirt-devel
mailing list