console-launcher 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/bin/console-launcher +12 -27
- data/lib/console-launcher.rb +142 -89
- data/lib/console-launcher_version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTU4NDAyMmFiMTAwNDZjYjNiODRkMTRmODg1MDcxOGFhZjNhYjFkMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjQ2ODFhMWNjZWIwNTg3ZjQ0MzIyMmRkNjFjMWJhZTFlNDIxODk5ZQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTQ4MDA2ZjEwZDAyZWJmZDMyYzgxMDliNGVkZDNjOWYwOGMzNWNmMmM1Y2U0
|
10
|
+
ZjdjYmU2MGEzYzc4ZDZiNDI0OWRjYjVjZTkyMjRmYzMzZDViYmFjZTFiNzFk
|
11
|
+
MzkxZmEzZjcyMDNmNjJlMTlmMWUyODExYTQ5ZjhmNTg0NmE4Njc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZWYzM2U2ZWFjZDlkMWQ3MjhjMWM4MDBjOWE5OTg1ODIxZmU1YTA3MTQ2NzIx
|
14
|
+
ZTE2YzE2NzgxN2Y5MzkyMjI0ZGVmMzZlNTYwNmQyODhkNjYzZTMwYzM4Y2M4
|
15
|
+
Zjg5NTUwMDc0ZmQ5ODdkYWVlY2JjZWM3ZDIyYjBkNGRmMWRiN2M=
|
data/bin/console-launcher
CHANGED
@@ -35,45 +35,30 @@ require 'optparse'
|
|
35
35
|
require 'yaml'
|
36
36
|
include Helper
|
37
37
|
|
38
|
-
|
39
|
-
print: false,
|
40
|
-
dryrun: false,
|
41
|
-
host: nil,
|
42
|
-
user: "admin@internal",
|
43
|
-
pass: nil
|
44
|
-
}
|
45
|
-
|
46
|
-
CONFIG_FILE = File.join(ENV['HOME'], '.config-launcher.rc.yaml')
|
47
|
-
if File.exists? CONFIG_FILE
|
48
|
-
config_options = YAML.load_file(CONFIG_FILE)
|
49
|
-
options.merge!(config_options)
|
50
|
-
else
|
51
|
-
File.open(CONFIG_FILE, 'w') { |file| YAML::dump(options, file) }
|
52
|
-
STDERR.puts "Initialized configuration file in #{CONFIG_FILE}"
|
53
|
-
end
|
38
|
+
Helper.load_options
|
54
39
|
|
55
40
|
optparse = OptionParser.new do |opts|
|
56
41
|
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
57
42
|
|
58
43
|
|
59
44
|
opts.on('--print', 'Print the command that is called to launch the Console Session') do
|
60
|
-
|
45
|
+
Helper::OPTIONS[:print] = true
|
61
46
|
end
|
62
47
|
|
63
48
|
opts.on('-d', '--dry-run', 'Do not execute the Remote Viewer Application') do
|
64
|
-
|
49
|
+
Helper::OPTIONS[:dryrun] = true
|
65
50
|
end
|
66
51
|
|
67
52
|
opts.on('-h', '--host HOSTNAME', 'The Hostname of your RHEV-M Installation') do |host|
|
68
|
-
|
53
|
+
Helper::OPTIONS[:host] = strip_url(host)
|
69
54
|
end
|
70
55
|
|
71
56
|
opts.on('-u', '--username USERNAME', 'The Username used to establish the connection to --host (defaults to admin@internal)') do |u|
|
72
|
-
|
57
|
+
Helper::OPTIONS[:user] = u
|
73
58
|
end
|
74
59
|
|
75
60
|
opts.on('-p', '--password PASSWORD', 'The Password used to establish the connection to --host') do |pass|
|
76
|
-
|
61
|
+
Helper::OPTIONS[:pass] = pass
|
77
62
|
end
|
78
63
|
|
79
64
|
# This displays the help screen, all programs are
|
@@ -102,15 +87,15 @@ end
|
|
102
87
|
# the @options. What's left is the list of files to resize.
|
103
88
|
optparse.parse!
|
104
89
|
|
105
|
-
if
|
90
|
+
if Helper::OPTIONS[:host] == nil
|
106
91
|
puts "ERROR: You have to configure RHEV-M Hostname to connect to"
|
107
92
|
puts optparse.help
|
108
93
|
exit 1
|
109
94
|
end
|
110
95
|
|
111
|
-
|
96
|
+
Helper::OPTIONS[:pass] = get_password() if Helper::OPTIONS[:pass] == nil
|
112
97
|
|
113
|
-
rhevm = RhevManager.new(
|
98
|
+
rhevm = RhevManager.new(Helper::OPTIONS[:host], Helper::OPTIONS[:user], Helper::OPTIONS[:pass])
|
114
99
|
|
115
100
|
while true do
|
116
101
|
begin
|
@@ -118,7 +103,7 @@ while true do
|
|
118
103
|
|
119
104
|
# Print the selection to the User
|
120
105
|
puts
|
121
|
-
puts "Running Virtual Machines found for #{
|
106
|
+
puts "Running Virtual Machines found for #{Helper::OPTIONS[:host]}:"
|
122
107
|
vms.each_with_index do |v, index|
|
123
108
|
puts "#{index+1}. Name: #{v.name} Description: #{v.description} State: #{v.state}"
|
124
109
|
end
|
@@ -140,8 +125,8 @@ while true do
|
|
140
125
|
|
141
126
|
if (1..vms.size).member?(index)
|
142
127
|
command = rhevm.launch_viewer(index)
|
143
|
-
puts command if
|
144
|
-
unless
|
128
|
+
puts command if Helper::OPTIONS[:print]
|
129
|
+
unless Helper::OPTIONS[:dryrun]
|
145
130
|
pid = Process.fork
|
146
131
|
|
147
132
|
if pid.nil?
|
data/lib/console-launcher.rb
CHANGED
@@ -12,6 +12,12 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
+
# Author:: Juergen Hoffmann (mailto:buddy@redhat.com)
|
16
|
+
# Author:: Vinny Valdez (mailto:vvaldez@redhat.com)
|
17
|
+
# Author:: Thomas Crowe (mailto:tcrowe@redhat.com)
|
18
|
+
# Copyright:: Copyright (c) 2013 Red Hat Inc.
|
19
|
+
# License:: http://www.apache.org/licenses/LICENSE-2.0
|
20
|
+
|
15
21
|
require 'rubygems'
|
16
22
|
require 'rest_client'
|
17
23
|
require 'xmlsimple'
|
@@ -19,121 +25,168 @@ require 'tmpdir' # required to download the certificate files on they fly
|
|
19
25
|
require 'net/http'
|
20
26
|
require 'highline/import' # Secure Password Prompting if a user does not provide it when the script is called
|
21
27
|
require 'rhev-manager/virtual-machine'
|
28
|
+
require 'rbconfig'
|
29
|
+
|
30
|
+
# This class provides utility methods to encapsulates RESTful access to the RHEV Manager.
|
31
|
+
# Since the returned response is in XML this class transforms the XML Response into Ruby Objects.
|
32
|
+
|
33
|
+
class RhevManager
|
34
|
+
|
35
|
+
TMP_DIR = Dir.tmpdir
|
36
|
+
|
37
|
+
def initialize(host, user, password)
|
38
|
+
@host = host
|
39
|
+
@user = user
|
40
|
+
@pass = password
|
41
|
+
|
42
|
+
# Create a little helper object that we will use to
|
43
|
+
# make connections to the REST API
|
44
|
+
@rhevm = RestClient::Resource.new(
|
45
|
+
"https://" + @host,
|
46
|
+
:user => @user,
|
47
|
+
:password => @pass,
|
48
|
+
:ssl_ca_cert => @cert,
|
49
|
+
:ssl_version => "SSLv3",
|
50
|
+
:verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
51
|
+
get_cert
|
52
|
+
end
|
22
53
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# Create a little helper object that we will use to
|
33
|
-
# make connections to the REST API
|
34
|
-
@rhevm = RestClient::Resource.new(
|
35
|
-
"https://" + @host,
|
36
|
-
:user => @user,
|
37
|
-
:password => @pass,
|
38
|
-
:ssl_ca_cert => @cert,
|
39
|
-
:ssl_version => "SSLv3",
|
40
|
-
:verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
41
|
-
get_cert
|
42
|
-
end
|
43
|
-
|
44
|
-
def get_cert()
|
45
|
-
# download the certificate file on the fly
|
46
|
-
begin
|
47
|
-
cert = File.new(TMP_DIR + "/" + @host + ".crt", "w+")
|
48
|
-
Net::HTTP.start(@host) do |http|
|
49
|
-
begin
|
50
|
-
http.request_get('/ca.crt') do |resp|
|
51
|
-
resp.read_body do |segment|
|
52
|
-
cert.write(segment)
|
53
|
-
end
|
54
|
+
# Download the Server SSL Certificate file on the fly
|
55
|
+
def get_cert()
|
56
|
+
begin
|
57
|
+
cert = File.new(TMP_DIR + "/" + @host + ".crt", "w+")
|
58
|
+
Net::HTTP.start(@host) do |http|
|
59
|
+
begin
|
60
|
+
http.request_get('/ca.crt') do |resp|
|
61
|
+
resp.read_body do |segment|
|
62
|
+
cert.write(segment)
|
54
63
|
end
|
55
|
-
ensure
|
56
|
-
cert.close()
|
57
64
|
end
|
65
|
+
ensure
|
66
|
+
cert.close()
|
58
67
|
end
|
59
|
-
@cert = cert.path
|
60
|
-
rescue => e
|
61
|
-
raise "There has been an error downloading the certificate file from #{@host}: #{e.message}"
|
62
68
|
end
|
69
|
+
@cert = cert.path
|
70
|
+
rescue => e
|
71
|
+
raise "There has been an error downloading the certificate file from #{@host}: #{e.message}"
|
63
72
|
end
|
73
|
+
end
|
64
74
|
|
65
|
-
|
66
|
-
@vms = Array.new # Clear out array
|
67
|
-
# get the vms api and get the list of vms
|
68
|
-
vms_data = XmlSimple.xml_in(@rhevm["/api/vms"].get.body, {'ForceArray' => false})
|
69
|
-
|
70
|
-
# Iterate through the VM's and get all the
|
71
|
-
# required information
|
72
|
-
vms_data['vm'].each do |vm|
|
73
|
-
# Making sure we only consider VM's that are in state up (so they do have a console to connect to)
|
74
|
-
# and that have the spice protocol enabled as the connection mode
|
75
|
-
if vm['status']['state'] == "up" && vm['display']['type'] == "spice"
|
76
|
-
@vms.push(VirtualMachine.new(vm))
|
77
|
-
end
|
78
|
-
end
|
79
|
-
return @vms
|
80
|
-
end
|
75
|
+
# Returns an array of VirtualMachine ruby objects that are running on the RHEV Manager
|
81
76
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
host_subject = hosts_data['certificate']['subject']
|
88
|
-
|
89
|
-
ticket_data = XmlSimple.xml_in(@rhevm["/api/vms/" + vm.id + "/ticket"].post("<action><ticket><expiry>30</expiry></ticket></action>", :content_type => 'application/xml').body, {'ForceArray' => false})
|
90
|
-
password = ticket_data['ticket']['value']
|
91
|
-
|
92
|
-
# Creating the .vv File for the connection
|
93
|
-
# download the certificate file on the fly
|
94
|
-
@vv = File.new(TMP_DIR + "/" + vm.name + ".vv", "w+")
|
95
|
-
begin
|
96
|
-
@vv.puts("[virt-viewer]")
|
97
|
-
@vv.puts("type=spice")
|
98
|
-
@vv.puts("host=#{vm.address}")
|
99
|
-
@vv.puts("port=#{vm.port}")
|
100
|
-
@vv.puts("password=#{password}")
|
101
|
-
@vv.puts("tls-port=#{vm.secure_port}")
|
102
|
-
@vv.puts("fullscreen=0")
|
103
|
-
@vv.puts("title=vm:#{vm.name} - %d - Press SHIFT+F12 to Release Cursor")
|
104
|
-
@vv.puts("enable-smartcard=0")
|
105
|
-
@vv.puts("enable-usb-autoshare=1")
|
106
|
-
@vv.puts("usb-filter=-1,-1,-1,-1,0")
|
107
|
-
@vv.puts("host-subject=#{host_subject}")
|
108
|
-
@vv.puts("toggle-fullscreen=shift+f11")
|
109
|
-
@vv.puts("release-cursor=shift+f12")
|
110
|
-
ensure
|
111
|
-
@vv.close()
|
112
|
-
end
|
77
|
+
def get_vms()
|
78
|
+
@vms = Array.new # Clear out array
|
79
|
+
|
80
|
+
# get the vms api and get the list of vms
|
81
|
+
vms_data = XmlSimple.xml_in(@rhevm["/api/vms"].get.body, {'ForceArray' => false})
|
113
82
|
|
114
|
-
|
115
|
-
|
83
|
+
# Iterate through the VM's and get all the
|
84
|
+
# required information
|
85
|
+
vms_data['vm'].each do |vm|
|
86
|
+
# Making sure we only consider VM's that are in state up (so they do have a console to connect to)
|
87
|
+
# and that have the spice protocol enabled as the connection mode
|
88
|
+
if vm['status']['state'] == "up" && vm['display']['type'] == "spice"
|
89
|
+
@vms.push(VirtualMachine.new(vm))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return @vms
|
93
|
+
end
|
116
94
|
|
117
|
-
|
95
|
+
# This method takes an index that is used to query the VirtualMachine Array for the VM that is supposed to
|
96
|
+
# be started. It then queries the RHEV Manager for the correct subject and then requests a ticket for the
|
97
|
+
# console session.
|
98
|
+
# It then creates a .vv file that is used to be passed to the RemoteViewer Application. It returns the command
|
99
|
+
# to be launched because we fork the process and detach from the RemoteViewer process so we are able to launch
|
100
|
+
# more than one Console Session at once.
|
101
|
+
def launch_viewer(index)
|
102
|
+
vm = @vms[index-1]
|
103
|
+
|
104
|
+
# let us no gather the host subject
|
105
|
+
hosts_data = XmlSimple.xml_in(@rhevm["/api/hosts/"+vm.host_uuid].get.body, {'ForceArray' => false})
|
106
|
+
host_subject = hosts_data['certificate']['subject']
|
107
|
+
|
108
|
+
ticket_data = XmlSimple.xml_in(@rhevm["/api/vms/" + vm.id + "/ticket"].post("<action><ticket><expiry>30</expiry></ticket></action>", :content_type => 'application/xml').body, {'ForceArray' => false})
|
109
|
+
password = ticket_data['ticket']['value']
|
110
|
+
|
111
|
+
# Creating the .vv File for the connection
|
112
|
+
# download the certificate file on the fly
|
113
|
+
@vv = File.new(TMP_DIR + "/" + vm.name + ".vv", "w+")
|
114
|
+
begin
|
115
|
+
@vv.puts("[virt-viewer]")
|
116
|
+
@vv.puts("type=spice")
|
117
|
+
@vv.puts("host=#{vm.address}")
|
118
|
+
@vv.puts("port=#{vm.port}")
|
119
|
+
@vv.puts("password=#{password}")
|
120
|
+
@vv.puts("tls-port=#{vm.secure_port}")
|
121
|
+
@vv.puts("fullscreen=0")
|
122
|
+
@vv.puts("title=vm:#{vm.name} - %d - Press SHIFT+F12 to Release Cursor")
|
123
|
+
@vv.puts("enable-smartcard=0")
|
124
|
+
@vv.puts("enable-usb-autoshare=1")
|
125
|
+
@vv.puts("usb-filter=-1,-1,-1,-1,0")
|
126
|
+
@vv.puts("host-subject=#{host_subject}")
|
127
|
+
@vv.puts("toggle-fullscreen=shift+f11")
|
128
|
+
@vv.puts("release-cursor=shift+f12")
|
129
|
+
ensure
|
130
|
+
@vv.close()
|
118
131
|
end
|
119
132
|
|
133
|
+
# Now that we have all the information we can print the cmd line
|
134
|
+
puts "Console to VM: #{vm.name} state: #{vm.state} is started"
|
120
135
|
|
136
|
+
command = "#{Helper::OPTIONS[:viewer]} --spice-ca-file #{@cert} #{@vv.path}"
|
121
137
|
end
|
122
138
|
|
139
|
+
|
140
|
+
end
|
141
|
+
|
123
142
|
module Helper
|
124
143
|
|
144
|
+
CONFIG_FILE = File.join(ENV['HOME'], '.console-launcher.rc.yaml')
|
145
|
+
|
146
|
+
OPTIONS = {
|
147
|
+
print: false,
|
148
|
+
dryrun: false,
|
149
|
+
host: nil,
|
150
|
+
user: "admin@internal",
|
151
|
+
pass: nil
|
152
|
+
}
|
153
|
+
|
154
|
+
OPTIONS[:viewer] = case RbConfig::CONFIG['host_os']
|
155
|
+
when /mac|darwin/ then
|
156
|
+
"/Applications/RemoteViewer.app/Contents/MacOS/RemoteViewer"
|
157
|
+
when /linux|bsd|cygwin/ then
|
158
|
+
"/usr/bin/remote-viewer"
|
159
|
+
when /mswin|mingw/ then
|
160
|
+
"C:/Program Files/VirtViewer/bin/remote-viewer.exe"
|
161
|
+
# when /solaris|sunos/ then :Linux # needs testing..
|
162
|
+
else
|
163
|
+
raise "Your OS(#{ RbConfig::CONFIG['host_os'] }) is not yet supported"
|
164
|
+
end
|
165
|
+
|
166
|
+
def load_options
|
167
|
+
if File.exists? CONFIG_FILE
|
168
|
+
config_options = YAML.load_file(CONFIG_FILE)
|
169
|
+
OPTIONS.merge!(config_options)
|
170
|
+
|
171
|
+
unless config_options.has_key?(:viewer)
|
172
|
+
File.open(CONFIG_FILE, 'w') { |file| YAML::dump(OPTIONS, file) }
|
173
|
+
end
|
174
|
+
else
|
175
|
+
File.open(CONFIG_FILE, 'w') { |file| YAML::dump(OPTIONS, file) }
|
176
|
+
STDERR.puts "Initialized configuration file in #{CONFIG_FILE}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
125
180
|
# queries the User for a password
|
126
181
|
def get_password(prompt="RHEV-M Password: ")
|
127
182
|
ask(prompt) { |q| q.echo = "*" }
|
128
183
|
end
|
129
184
|
|
185
|
+
# Remove any leading http or https from the host
|
130
186
|
def strip_url(url)
|
131
|
-
# Remove any leading http or https from the host
|
132
187
|
url = url.split("://")[1] if url.include? "://"
|
133
188
|
return url
|
134
189
|
end
|
135
190
|
|
136
|
-
def initialize
|
137
191
|
|
138
|
-
end
|
139
192
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: console-launcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juergen Hoffmann
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-05-
|
13
|
+
date: 2013-05-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rest-client
|