Skip to content

Commit

Permalink
Add support for vCloud console access via WebMKS
Browse files Browse the repository at this point in the history
With this commit we provide code needed for VMware vCloud Director's (i.e.
CloudManager) console access to work. The code is similar to that of InfraManger,
but with some important differences:

* WebMKS SDK requires additional information (vmx location)
* WebMKS SDK must know it's vCloud's VM to use uint8utf8 protocol (instead binary)
* websocket proxy must connect to differently formed URL (port is part of URL, while
  actual port is always 443)

Signed-off-by: Miha Pleško <miha.plesko@xlab.si>
  • Loading branch information
miha-plesko committed Mar 28, 2018
1 parent 39f5e4e commit 5c3f143
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions app/models/manageiq/providers/vmware/cloud_manager/vm.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class ManageIQ::Providers::Vmware::CloudManager::Vm < ManageIQ::Providers::CloudManager::Vm
include_concern 'Operations'
include_concern 'RemoteConsole'

supports :snapshots
supports :remove_all_snapshots
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module ManageIQ::Providers::Vmware::CloudManager::Vm::RemoteConsole
require_dependency 'securerandom'

def console_supported?(type)
%w(WEBMKS).include?(type.upcase)
end

def validate_remote_console_acquire_ticket(protocol, options = {})
raise(MiqException::RemoteConsoleNotSupportedError, "#{protocol} remote console requires the vm to be registered with a management system.") if ext_management_system.nil?
options[:check_if_running] = true unless options.key?(:check_if_running)
raise(MiqException::RemoteConsoleNotSupportedError, "#{protocol} remote console requires the vm to be running.") if options[:check_if_running] && state != "on"
end

def remote_console_acquire_ticket(userid, originating_server, protocol)
send("remote_console_#{protocol.to_s.downcase}_acquire_ticket", userid, originating_server)
end

def remote_console_acquire_ticket_queue(protocol, userid)
task_opts = {
:action => "Acquiring Vm #{name} #{protocol.to_s.upcase} remote console ticket for user #{userid}",
:userid => userid
}

queue_opts = {
:class_name => self.class.name,
:instance_id => id,
:method_name => 'remote_console_acquire_ticket',
:priority => MiqQueue::HIGH_PRIORITY,
:role => 'ems_operations',
:zone => my_zone,
:args => [userid, MiqServer.my_server.id, protocol]
}

MiqTask.generic_action_with_callback(task_opts, queue_opts)
end

#
# WebMKS
#

def remote_console_webmks_acquire_ticket(userid, originating_server = nil)
validate_remote_console_webmks_support
ticket = nil
ext_management_system.with_provider_connection do |service|
ticket = service.post_acquire_mks_ticket(ems_ref).body
end
raise(MiqException::RemoteConsoleNotSupportedError, 'Could not obtain WebMKS ticket') unless ticket && ticket[:Ticket]

SystemConsole.force_vm_invalid_token(id)

console_args = {
:user => User.find_by(:userid => userid),
:vm_id => id,
:ssl => true,
:protocol => 'webmks-uint8utf8',
:secret => ticket[:Ticket],
:url_secret => SecureRandom.hex,
:url => "/#{ticket[:Port]};#{ticket[:Ticket]}"
}
SystemConsole.launch_proxy_if_not_local(console_args, originating_server, ticket[:Host], 443).update(
:secret => 'is-in-url',
# vCloud specific querystring params
:is_vcloud => true,
:vmx => ticket[:Vmx]
)
end

def validate_remote_console_webmks_support
validate_remote_console_acquire_ticket('webmks')
if (api_version = ext_management_system.api_version.to_f) && api_version < 5.5
raise(MiqException::RemoteConsoleNotSupportedError, "vCloud version #{api_version} does not support WebMKS remote console.")
end
true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
describe ManageIQ::Providers::Vmware::CloudManager::Vm::RemoteConsole do
let(:user) { FactoryGirl.create(:user) }
let(:ems) { FactoryGirl.create(:ems_vmware_cloud, :api_version => 5.5) }
let(:vm) { FactoryGirl.create(:vm_vcloud, :ext_management_system => ems, :raw_power_state => 'on') }

context '#remote_console_acquire_ticket' do
it 'with :webmks' do
expect(vm).to receive(:remote_console_webmks_acquire_ticket).with(user.userid, 1)
vm.remote_console_acquire_ticket(user.userid, 1, :webmks)
end
end

context '#remote_console_acquire_ticket_queue' do
let(:server) { double('MiqServer') }

before(:each) do
allow(vm).to receive_messages(:my_zone => nil)
allow(server).to receive_messages(:my_zone => nil)
allow(server).to receive_messages(:id => 1)
allow(MiqServer).to receive_messages(:my_server => server)
end

it 'with :webmks' do
vm.remote_console_acquire_ticket_queue(:webmks, user.userid)

q_all = MiqQueue.all
expect(q_all.length).to eq(1)
expect(q_all[0].method_name).to eq('remote_console_acquire_ticket')
expect(q_all[0].args).to eq([user.userid, 1, :webmks])
end
end

context '#remote_console_webmks_acquire_ticket' do
before(:each) do
allow(ems).to receive(:with_provider_connection).and_yield(connection)
allow(SecureRandom).to receive(:hex).and_return('hex')
end

let(:connection) { double('connection') }
let(:empty_response) { double('response', :body => {}) }
let(:response) do
double(
'response',
:body => {
:Ticket => 'ticket',
:Port => 1234,
:Host => 'host'
}
)
end

it 'performs validation' do
expect(connection).to receive(:post_acquire_mks_ticket).and_return(empty_response)
expect(vm).to receive(:validate_remote_console_webmks_support)
expect { vm.remote_console_webmks_acquire_ticket(user.userid) }.to raise_error MiqException::RemoteConsoleNotSupportedError
end

it 'launches proxy socket' do
expect(connection).to receive(:post_acquire_mks_ticket).and_return(response)
expect(SystemConsole).to receive(:launch_proxy_if_not_local).with(
{
:user => user,
:vm_id => vm.id,
:ssl => true,
:protocol => 'webmks-uint8utf8',
:secret => 'ticket',
:url_secret => 'hex',
:url => '/1234;ticket'
},
1, 'host', 443
).and_return({})
vm.remote_console_webmks_acquire_ticket(user.userid, 1)
end
end

context '#validate_remote_console_webmks_support' do
it 'normal case' do
ems.update_attribute(:api_version, '5.5')
expect(vm.validate_remote_console_webmks_support).to be_truthy
end

it 'with vm with no ems' do
vm.ext_management_system = nil
vm.save!
expect { vm.validate_remote_console_webmks_support }.to raise_error MiqException::RemoteConsoleNotSupportedError
end

it 'with vm off' do
vm.update_attribute(:raw_power_state, 'off')
expect { vm.validate_remote_console_webmks_support }.to raise_error MiqException::RemoteConsoleNotSupportedError
end

it 'on vCloud 5.1' do
ems.update_attribute(:api_version, '5.1')
expect { vm.validate_remote_console_webmks_support }.to raise_error MiqException::RemoteConsoleNotSupportedError
end
end
end

0 comments on commit 5c3f143

Please sign in to comment.