openshift 0.60.3
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.
- data/Error codes.txt +15 -0
- data/bin/os +42 -0
- data/bin/os-add-cartridge +190 -0
- data/bin/os-clone-application +175 -0
- data/bin/os-create-application +306 -0
- data/bin/os-create-environment +306 -0
- data/bin/os-delete-application +141 -0
- data/bin/os-delete-environment +140 -0
- data/bin/os-deregister-cloud +124 -0
- data/bin/os-help +108 -0
- data/bin/os-inspect-application +222 -0
- data/bin/os-list-applications +139 -0
- data/bin/os-list-cartridges +182 -0
- data/bin/os-list-clouds +137 -0
- data/bin/os-list-environments +188 -0
- data/bin/os-list-servers +122 -0
- data/bin/os-open-console +162 -0
- data/bin/os-register-cloud +181 -0
- data/bin/os-remove-cartridge +170 -0
- data/bin/os-restart-application +147 -0
- data/bin/os-start-application +147 -0
- data/bin/os-start-environment +144 -0
- data/bin/os-stop-application +147 -0
- data/bin/os-stop-environment +144 -0
- data/bin/os-tail-logs +159 -0
- data/conf/openshift.conf +5 -0
- data/lib/openshift.rb +666 -0
- metadata +192 -0
data/bin/os-tail-logs
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright 2010 Red Hat, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person
|
5
|
+
# obtaining a copy of this software and associated documentation files
|
6
|
+
# (the "Software"), to deal in the Software without restriction,
|
7
|
+
# including without limitation the rights to use, copy, modify, merge,
|
8
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
9
|
+
# and to permit persons to whom the Software is furnished to do so,
|
10
|
+
# subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
19
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
20
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
|
24
|
+
require 'openshift'
|
25
|
+
|
26
|
+
def usage
|
27
|
+
puts <<USAGE
|
28
|
+
== Synopsis
|
29
|
+
|
30
|
+
os-tail-logs: Tail or download log files from an environment
|
31
|
+
|
32
|
+
== Usage
|
33
|
+
|
34
|
+
os tail-logs [options] [NAME]
|
35
|
+
|
36
|
+
-u|--username
|
37
|
+
Redhat Login (RHN or OpenShift login with OpenShift Express access)
|
38
|
+
|
39
|
+
-p|--password
|
40
|
+
Redhat Password
|
41
|
+
|
42
|
+
-t|--target flex|express
|
43
|
+
The cloud platform the environment is running on.
|
44
|
+
|
45
|
+
-h|--help
|
46
|
+
Prints this message
|
47
|
+
|
48
|
+
NAME: The name or GUID of the environment to get logs from.
|
49
|
+
USAGE
|
50
|
+
end
|
51
|
+
|
52
|
+
opts = GetoptLong.new(
|
53
|
+
["--username", "-u", GetoptLong::REQUIRED_ARGUMENT],
|
54
|
+
["--password", "-p", GetoptLong::REQUIRED_ARGUMENT],
|
55
|
+
["--sso", GetoptLong::REQUIRED_ARGUMENT],
|
56
|
+
["--debug", GetoptLong::NO_ARGUMENT],
|
57
|
+
["--help", GetoptLong::NO_ARGUMENT],
|
58
|
+
["--porcelin", GetoptLong::NO_ARGUMENT]
|
59
|
+
)
|
60
|
+
|
61
|
+
args = {}
|
62
|
+
begin
|
63
|
+
opts.each{ |k,v| args[k]=v }
|
64
|
+
rescue GetoptLong::Error => e
|
65
|
+
usage
|
66
|
+
exit -100
|
67
|
+
end
|
68
|
+
|
69
|
+
@debug = true if args['--debug']
|
70
|
+
args['--target'] = conf('default_target') || 'flex' if args['--target'].nil? or args['--target']==""
|
71
|
+
debug "Target platform #{args['--target']}"
|
72
|
+
|
73
|
+
if args['--help']
|
74
|
+
usage
|
75
|
+
exit
|
76
|
+
end
|
77
|
+
|
78
|
+
if args['--target'] == 'flex'
|
79
|
+
flex_server = conf('flex_server')
|
80
|
+
cookie = args['--sso']
|
81
|
+
if !cookie
|
82
|
+
username = args['--username'] || conf("username") || Openshift::IO.prompt("Redhat username",[],Openshift::Validation.method(:check_login))
|
83
|
+
password = args['--password'] || Openshift::IO.prompt("Redhat password",nil,nil,true,false)
|
84
|
+
csay("Logging into Openshift Flex as #{username}\n",:message)
|
85
|
+
cookie=Openshift.login(@http,username,password)
|
86
|
+
end
|
87
|
+
|
88
|
+
environment_id = ARGV.shift
|
89
|
+
debug "Tailing environment: #{environment_id}"
|
90
|
+
if not environment_id
|
91
|
+
csay("No environment specified.",:error)
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
|
95
|
+
csay("Fetching environment list... ")
|
96
|
+
environments=nil
|
97
|
+
begin
|
98
|
+
environments=`os-list-environments --sso "#{cookie}" --porcelin`
|
99
|
+
environments = JSON.parse(environments)
|
100
|
+
csay("[OK]",:conf)
|
101
|
+
rescue Exception => e
|
102
|
+
debug environments
|
103
|
+
debug e
|
104
|
+
csay("[ERROR]",:error)
|
105
|
+
csay("Error retrieving environment list.")
|
106
|
+
exit -400
|
107
|
+
end
|
108
|
+
candidates = environments.find_all{ |c| c["name"]==environment_id or c["id"]==environment_id }
|
109
|
+
|
110
|
+
if candidates.size == 0
|
111
|
+
csay("Unable to find environment identified by #{environment_id}",:error)
|
112
|
+
exit -200
|
113
|
+
end
|
114
|
+
|
115
|
+
if candidates.size > 1
|
116
|
+
csay("Multiple environments are named #{environment_id}. Please provide the environment Id",:error)
|
117
|
+
exit -201
|
118
|
+
end
|
119
|
+
|
120
|
+
environment = candidates[0]
|
121
|
+
csay("Tailing logs for environment ")
|
122
|
+
csay("#{environment["name"]} ",:emphasis)
|
123
|
+
csay("...")
|
124
|
+
lastEntry = nil
|
125
|
+
params = nil
|
126
|
+
while(true)
|
127
|
+
params = { "start-time" => lastEntry['timestamp'].to_i, "start-index" => lastEntry['index'].to_i} if not lastEntry.nil?
|
128
|
+
uri = URI.parse("https://#{environment['dns']}:4242/monitoring/logs")
|
129
|
+
response = Openshift::Rest.get(@http, uri, params,
|
130
|
+
nil, {'user' => environment['username'], 'password' => environment['password']})
|
131
|
+
case response
|
132
|
+
when Net::HTTPSuccess
|
133
|
+
|
134
|
+
else
|
135
|
+
debug "HTTP code: #{response.code}"
|
136
|
+
debug response.body
|
137
|
+
csay("[ERROR]",:error) if not @porcelin
|
138
|
+
csay("Unable to retrieve application logs for environment.",:error)
|
139
|
+
end
|
140
|
+
logs = JSON.parse(response.body)
|
141
|
+
logs = logs['logs']
|
142
|
+
lastEntry = logs['last-entry']
|
143
|
+
entries = logs['entries']
|
144
|
+
|
145
|
+
if entries.size ==0
|
146
|
+
csay("No more logs..sleeping 5 sec..")
|
147
|
+
sleep(5)
|
148
|
+
end
|
149
|
+
entries.each{ |entry|
|
150
|
+
csay("[#{Time.at(entry['timestamp']).utc}] ")
|
151
|
+
csay("[#{entry['app_id']}] ")
|
152
|
+
csay(entry['text'])
|
153
|
+
}
|
154
|
+
lastEntry['index'] = lastEntry['index'].to_i + 1
|
155
|
+
debug lastEntry
|
156
|
+
end
|
157
|
+
else
|
158
|
+
csay("This feature is currently not implemented for Openshift Express applications.\n",:red)
|
159
|
+
end
|
data/conf/openshift.conf
ADDED
data/lib/openshift.rb
ADDED
@@ -0,0 +1,666 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright 2010 Red Hat, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person
|
5
|
+
# obtaining a copy of this software and associated documentation files
|
6
|
+
# (the "Software"), to deal in the Software without restriction,
|
7
|
+
# including without limitation the rights to use, copy, modify, merge,
|
8
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
9
|
+
# and to permit persons to whom the Software is furnished to do so,
|
10
|
+
# subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
19
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
20
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'fileutils'
|
26
|
+
require 'getoptlong'
|
27
|
+
require 'json'
|
28
|
+
require 'net/http'
|
29
|
+
require 'net/https'
|
30
|
+
require 'net/http/post/multipart'
|
31
|
+
require 'parseconfig'
|
32
|
+
require 'resolv'
|
33
|
+
require 'uri'
|
34
|
+
require 'highline/import'
|
35
|
+
require 'cgi'
|
36
|
+
|
37
|
+
@libra_kfile = "#{ENV['HOME']}/.ssh/libra_id_rsa"
|
38
|
+
@libra_kpfile = "#{ENV['HOME']}/.ssh/libra_id_rsa.pub"
|
39
|
+
@conf_name = 'openshift.conf'
|
40
|
+
|
41
|
+
_linux_cfg = '/etc/openshift/' + @conf_name
|
42
|
+
_gem_cfg = File.join(File.expand_path(File.dirname(__FILE__) + "/../conf"), @conf_name)
|
43
|
+
_home_conf = File.expand_path('~/.openshift')
|
44
|
+
@local_config_path = File.join(_home_conf, @conf_name)
|
45
|
+
@config_path = File.exists?(_linux_cfg) ? _linux_cfg : _gem_cfg
|
46
|
+
|
47
|
+
FileUtils.mkdir_p _home_conf unless File.directory?(_home_conf)
|
48
|
+
local_config_path = File.expand_path(@local_config_path)
|
49
|
+
if !File.exists? local_config_path
|
50
|
+
FileUtils.touch local_config_path
|
51
|
+
puts ""
|
52
|
+
puts "Created local config file: " + local_config_path
|
53
|
+
puts "openshift.conf contains user configuration and can be transferred across clients."
|
54
|
+
puts ""
|
55
|
+
end
|
56
|
+
|
57
|
+
begin
|
58
|
+
$global_config = ParseConfig.new(@config_path)
|
59
|
+
$local_config = ParseConfig.new(File.expand_path(@local_config_path))
|
60
|
+
rescue Errno::EACCES => e
|
61
|
+
puts "Could not open config file: #{e.message}"
|
62
|
+
exit 253
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check for proxy environment
|
66
|
+
if ENV['http_proxy']
|
67
|
+
host, port = ENV['http_proxy'].split(':')
|
68
|
+
@http = Net::HTTP::Proxy(host, port)
|
69
|
+
else
|
70
|
+
@http = Net::HTTP
|
71
|
+
end
|
72
|
+
|
73
|
+
def conf(name)
|
74
|
+
val = $local_config.get_value(name) ? $local_config.get_value(name) : $global_config.get_value(name)
|
75
|
+
val.gsub!(/\\:/,":") if not val.nil?
|
76
|
+
#print "CONF #{name} => #{val}\n\n"
|
77
|
+
val
|
78
|
+
end
|
79
|
+
|
80
|
+
def setconf(name,value)
|
81
|
+
$local_config.add(name,value)
|
82
|
+
$local_config.write(File.open(File.expand_path(@local_config_path),"w"))
|
83
|
+
end
|
84
|
+
|
85
|
+
HighLine.track_eof=false
|
86
|
+
HighLine.color_scheme = HighLine::ColorScheme.new do |cs|
|
87
|
+
cs[:emphasis] = [ :blue, :bold ]
|
88
|
+
cs[:error] = [ :red ]
|
89
|
+
cs[:warn] = [ :red ]
|
90
|
+
cs[:debug] = [ :red, :on_white, :bold ]
|
91
|
+
cs[:conf] = [ :green ]
|
92
|
+
cs[:question] = [ :magenta, :bold ]
|
93
|
+
cs[:table] = [ :blue ]
|
94
|
+
cs[:table_header] = [ :bold ]
|
95
|
+
cs[:message] = [ :bold ]
|
96
|
+
end
|
97
|
+
|
98
|
+
@h = HighLine.new
|
99
|
+
def csay(str,*options)
|
100
|
+
lastChar = str[-1..-1]
|
101
|
+
h = HighLine.new
|
102
|
+
if lastChar == ' ' or lastChar == '\t'
|
103
|
+
str=h.color(str[0..-2],*options)+lastChar
|
104
|
+
else
|
105
|
+
str=h.color(str,*options)
|
106
|
+
end
|
107
|
+
h.say(str)
|
108
|
+
end
|
109
|
+
|
110
|
+
def debug(*s)
|
111
|
+
@h = HighLine.new if @h.nil?
|
112
|
+
if @debug or conf('debug') == "true"
|
113
|
+
str = "DEBUG"
|
114
|
+
str += " "*(@h.output_cols()-str.length) if str.length < @h.output_cols()
|
115
|
+
csay(str,:debug)
|
116
|
+
s.each{ |line|
|
117
|
+
str = line.to_s
|
118
|
+
str += " "*(@h.output_cols()-str.length) + "\n" if str.length < @h.output_cols()
|
119
|
+
csay(str,:debug)
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
module Openshift
|
125
|
+
module SSH
|
126
|
+
def self.gen_ssh_keys(libra_kfile, libra_kpfile)
|
127
|
+
if File.readable?(libra_kfile)
|
128
|
+
puts "OpenShift ssh key found at #{libra_kfile}. Reusing..."
|
129
|
+
else
|
130
|
+
puts "Generating OpenShift ssh key to #{libra_kfile}"
|
131
|
+
# Use system for interaction
|
132
|
+
system("ssh-keygen -t rsa -f '#{libra_kfile}'")
|
133
|
+
end
|
134
|
+
ssh_key = File.open(libra_kpfile).gets.chomp.split(' ')[1]
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.setup_ssh_config(domain)
|
138
|
+
ssh_config = "#{ENV['HOME']}/.ssh/config"
|
139
|
+
ssh_config_d = "#{ENV['HOME']}/.ssh/"
|
140
|
+
# Check / add new host to ~/.ssh/config
|
141
|
+
puts "Checking ~/.ssh/config"
|
142
|
+
|
143
|
+
found = false
|
144
|
+
begin
|
145
|
+
File.open(ssh_config, "r") do |sline|
|
146
|
+
while(line = sline.gets)
|
147
|
+
if line.to_s.start_with? "Host *.#{domain}"
|
148
|
+
found = true
|
149
|
+
break
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
rescue Errno::EACCES
|
154
|
+
puts "Could not read from #{ssh_config}"
|
155
|
+
puts "Reason: " + $!
|
156
|
+
puts
|
157
|
+
puts "Please correct this first. Then run rerun."
|
158
|
+
puts
|
159
|
+
exit 213
|
160
|
+
rescue Errno::ENOENT
|
161
|
+
puts "Could not find #{ssh_config}. This is ok, continuing"
|
162
|
+
end
|
163
|
+
if found
|
164
|
+
puts "Found #{domain} in ~/.ssh/config... No need to adjust"
|
165
|
+
else
|
166
|
+
puts " Adding #{domain} to ~/.ssh/config"
|
167
|
+
begin
|
168
|
+
f = File.open(ssh_config, "a")
|
169
|
+
f.puts <<SSH
|
170
|
+
|
171
|
+
# Added by rhc-create-application on #{`date`}
|
172
|
+
Host *.#{domain}
|
173
|
+
IdentityFile ~/.ssh/libra_id_rsa
|
174
|
+
VerifyHostKeyDNS yes
|
175
|
+
StrictHostKeyChecking no
|
176
|
+
UserKnownHostsFile ~/.ssh/libra_known_hosts
|
177
|
+
|
178
|
+
SSH
|
179
|
+
f.close
|
180
|
+
rescue Errno::EACCES
|
181
|
+
puts "Could not write to #{ssh_config}"
|
182
|
+
puts "Reason: " + $!
|
183
|
+
puts
|
184
|
+
puts "Please correct this first. Then run rerun."
|
185
|
+
puts
|
186
|
+
exit 214
|
187
|
+
rescue Errno::ENOENT
|
188
|
+
# Make directory and config if they do not exist
|
189
|
+
puts "Could not find directory: " + $!
|
190
|
+
puts "creating"
|
191
|
+
FileUtils.mkdir_p ssh_config_d
|
192
|
+
file = File.open(ssh_config, 'w')
|
193
|
+
file.close
|
194
|
+
retry
|
195
|
+
end
|
196
|
+
end
|
197
|
+
File.chmod(0700, ssh_config_d)
|
198
|
+
File.chmod(0600, ssh_config)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
module Validation
|
203
|
+
Maxdlen = 16
|
204
|
+
|
205
|
+
TYPES = {
|
206
|
+
'php-5.3' => :php,
|
207
|
+
'perl-5.10' => :perl,
|
208
|
+
'rack-1.1' => :rack,
|
209
|
+
'wsgi-3.2' => :wsgi,
|
210
|
+
'jbossas-7.0' => :jbossas
|
211
|
+
}
|
212
|
+
|
213
|
+
EXPRESS_SUPPORTED_TYPES = {
|
214
|
+
'php-5.3' => :php,
|
215
|
+
'rack-1.1' => :rack,
|
216
|
+
'wsgi-3.2' => :wsgi
|
217
|
+
}
|
218
|
+
|
219
|
+
def self.get_supportted_templates(sep=', ', target="express")
|
220
|
+
if target == "express"
|
221
|
+
return get_cartridge_types(EXPRESS_SUPPORTED_TYPES)
|
222
|
+
else
|
223
|
+
return get_cartridge_types(FLEX_SUPPORTED_TYPES)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.check_login(login)
|
228
|
+
if login
|
229
|
+
if login.length < 6
|
230
|
+
csay('Login must be at least 6 characters\n',:error)
|
231
|
+
return false
|
232
|
+
elsif login =~ /["\$\^<>\|%\/;:,\\\*=~]/
|
233
|
+
csay('Login may not contain any of these characters: (\") ($) (^) (<) (>) (|) (%) (/) (;) (:) (,) (\) (*) (=) (~)\n',:error)
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
else
|
237
|
+
csay("Login is required\n",:error)
|
238
|
+
return false
|
239
|
+
end
|
240
|
+
true
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.check_field(field, type, max=0, space_ok=false)
|
244
|
+
if field
|
245
|
+
if space_ok
|
246
|
+
if field =~ /[^0-9a-zA-Z ]/
|
247
|
+
csay("#{type} contains non-alphanumeric characters!\n",:error)
|
248
|
+
return false
|
249
|
+
end
|
250
|
+
else
|
251
|
+
if field =~ /[^0-9a-zA-Z]/
|
252
|
+
csay("#{type} contains non-alphanumeric characters!\n",:error)
|
253
|
+
return false
|
254
|
+
end
|
255
|
+
end
|
256
|
+
if max != 0 && field.length > max
|
257
|
+
csay("maximum #{type} size is #{max} characters\n",:error)
|
258
|
+
return false
|
259
|
+
end
|
260
|
+
if field.strip.length == 0
|
261
|
+
csay("#{type} is required",:error)
|
262
|
+
return false
|
263
|
+
end
|
264
|
+
else
|
265
|
+
csay("#{type} is required",:error)
|
266
|
+
return false
|
267
|
+
end
|
268
|
+
true
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
module IO
|
273
|
+
def self.prompt(prompt, options=nil,func=nil,required=false,echo=true,limit=nil)
|
274
|
+
input = nil
|
275
|
+
while input == nil
|
276
|
+
if options.nil? or options.length == 0
|
277
|
+
csay("#{prompt}? ",:question)
|
278
|
+
input = ask(""){ |q|
|
279
|
+
q.echo = "*" if !echo
|
280
|
+
q.limit = limit if !limit.nil?
|
281
|
+
}
|
282
|
+
else
|
283
|
+
csay("#{prompt} [#{options.join(',')}]? default: ",:question)
|
284
|
+
input = ask(""){ |q|
|
285
|
+
q.default = options[0] if !options.nil? and options.length > 0
|
286
|
+
}
|
287
|
+
if options.index(input).nil?
|
288
|
+
csay("Invalid value: #{input}. Please choose from ",:error)
|
289
|
+
csay("[#{options.join(',')}]",:emphasis)
|
290
|
+
input = nil
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
if func and not func.call(input)
|
295
|
+
input = nil
|
296
|
+
end
|
297
|
+
|
298
|
+
if required and (input.nil? or input.strip == "")
|
299
|
+
csay("This is required field. Please enter a value.",:error)
|
300
|
+
input = nil
|
301
|
+
end
|
302
|
+
end
|
303
|
+
input
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
module Formatter
|
308
|
+
def self.table( col_names, col_keys, col_sizes, rows, indent=0)
|
309
|
+
self.print_row_delim(col_sizes,indent)
|
310
|
+
print (" " * 4 * indent)
|
311
|
+
csay("| ",:table)
|
312
|
+
(0...col_names.size).each{ |i|
|
313
|
+
csay(sprintf("%#{col_sizes[i]}s ",col_names[i]),:table_header)
|
314
|
+
csay("| ",:table)
|
315
|
+
}
|
316
|
+
print "\n"
|
317
|
+
self.print_row_delim(col_sizes,indent)
|
318
|
+
rows.each{ |r|
|
319
|
+
print (" " * 4 * indent)
|
320
|
+
csay("| ",:table)
|
321
|
+
(0...col_names.size).each{ |i|
|
322
|
+
if not r[col_keys[i]].nil?
|
323
|
+
printf "%#{col_sizes[i]}s ", r[col_keys[i]]
|
324
|
+
else
|
325
|
+
printf "%#{col_sizes[i]}s ", " "
|
326
|
+
end
|
327
|
+
csay("| ", :table)
|
328
|
+
}
|
329
|
+
print "\n"
|
330
|
+
}
|
331
|
+
self.print_row_delim(col_sizes,indent)
|
332
|
+
end
|
333
|
+
|
334
|
+
def self.print_row_delim(col_sizes,indent)
|
335
|
+
print(" " * 4 * indent)
|
336
|
+
str = "+"
|
337
|
+
col_sizes.each{ |s|
|
338
|
+
str += "-"*(s+2)
|
339
|
+
str += "+"
|
340
|
+
}
|
341
|
+
csay(str,:table)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
module Rest
|
346
|
+
def self.execute(http, req, url, params=nil, cookies=nil, auth=nil)
|
347
|
+
cookies ||= ''
|
348
|
+
|
349
|
+
req['cookie']=""
|
350
|
+
cookies.each{ |cookie|
|
351
|
+
req['cookie'] += cookie
|
352
|
+
}
|
353
|
+
|
354
|
+
debug "---cookie---"
|
355
|
+
debug req['cookie']
|
356
|
+
debug "------------"
|
357
|
+
|
358
|
+
req.set_form_data(params) if params
|
359
|
+
req.basic_auth(auth['user'], auth['password']) if auth != nil
|
360
|
+
|
361
|
+
http = http.new(url.host, url.port)
|
362
|
+
#http.set_debug_output $stderr
|
363
|
+
if url.scheme == "https"
|
364
|
+
http.use_ssl = true
|
365
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
366
|
+
http.timeout = 60*10
|
367
|
+
end
|
368
|
+
http.open_timeout = 10
|
369
|
+
http.read_timeout = 60*10
|
370
|
+
begin
|
371
|
+
response = http.start {|http| http.request(req)}
|
372
|
+
response.each{|k,v|
|
373
|
+
debug "Header: #{k}:#{v}"
|
374
|
+
}
|
375
|
+
debug "Response code:#{response.code} body:#{response.body}"
|
376
|
+
response
|
377
|
+
rescue Exception => e
|
378
|
+
puts "There was a problem communicating with the server. Response message: #{e.message}"
|
379
|
+
exit 219
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.doHttp(http, method, url, params=nil, cookies=nil, auth=nil)
|
385
|
+
case method
|
386
|
+
when "POST"
|
387
|
+
post(http, url, params, cookies, auth)
|
388
|
+
when "PUT"
|
389
|
+
put(http, url, params, cookies, auth)
|
390
|
+
when "GET"
|
391
|
+
get(http, url, params, cookies, auth)
|
392
|
+
when "DELETE"
|
393
|
+
delete(http, url, params, cookies, auth)
|
394
|
+
else
|
395
|
+
get(http, url, params, cookies, auth)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def self.put(http, url, params=nil, cookies=nil, auth=nil)
|
400
|
+
req = http::Put.new(url.path)
|
401
|
+
execute(http, req, url, params, cookies, auth)
|
402
|
+
end
|
403
|
+
|
404
|
+
def self.get(http, url, params=nil, cookies='', auth=nil)
|
405
|
+
path = url.path
|
406
|
+
path += "?" + params.collect { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&') if not params.nil?
|
407
|
+
req = http::Get.new(path)
|
408
|
+
execute(http, req, url, nil, cookies, auth)
|
409
|
+
end
|
410
|
+
|
411
|
+
def self.postFile(http, url, params, fileParam, cookies=nil, auth=nil)
|
412
|
+
paramName=fileParam.keys[0]
|
413
|
+
fileName=fileParam[paramName]
|
414
|
+
response=nil
|
415
|
+
File.open(fileName) do |file|
|
416
|
+
params[paramName] = UploadIO.new(file, "application/octet-stream", File.basename(fileName))
|
417
|
+
req = Net::HTTP::Post::Multipart.new(url.path, params)
|
418
|
+
response = execute(http, req, url, nil, cookies, auth)
|
419
|
+
end
|
420
|
+
response
|
421
|
+
end
|
422
|
+
|
423
|
+
def self.post(http, url, params, cookies=nil, auth=nil)
|
424
|
+
req = http::Post.new(url.path)
|
425
|
+
execute(http, req, url, params, cookies, auth)
|
426
|
+
end
|
427
|
+
|
428
|
+
def self.delete(http, url, params, cookies=nil, auth=nil)
|
429
|
+
req = http::Delete.new(url.path)
|
430
|
+
execute(http, req, url, params, cookies, auth)
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
def self.login(http, username, password)
|
435
|
+
login_server = conf('login_server')
|
436
|
+
login_url = URI.parse("#{login_server}/wapps/streamline/login.html")
|
437
|
+
response = Rest.post(http, login_url, {'login' => username, 'password' => password})
|
438
|
+
case response
|
439
|
+
when Net::HTTPUnauthorized:
|
440
|
+
csay("Invalid username or password.\n", :red, :bold)
|
441
|
+
exit -1
|
442
|
+
end
|
443
|
+
debug response
|
444
|
+
response['Set-Cookie'].split("\; ")[0] + "\; "
|
445
|
+
end
|
446
|
+
|
447
|
+
def self.add_rhlogin_config(rhlogin, uuid=nil)
|
448
|
+
f = open(File.expand_path(@local_config_path), 'a')
|
449
|
+
|
450
|
+
unless @local_config.get_value('username')
|
451
|
+
f.puts("# Default rhlogin to use if none is specified")
|
452
|
+
f.puts("username=#{rhlogin}")
|
453
|
+
end
|
454
|
+
f.puts("#{rhlogin}=#{uuid}") unless @local_config.get_value('uuid') or uuid.nil?
|
455
|
+
f.close
|
456
|
+
end
|
457
|
+
|
458
|
+
module Flex
|
459
|
+
TEMPLATES = {
|
460
|
+
'php' => ['php-5', 'www-dynamic'],
|
461
|
+
'zend' => ['zend-server-php', 'www-dynamic'],
|
462
|
+
'zend_ce' => ['zend-server-ce', 'www-dynamic'],
|
463
|
+
'tomcat' => ['tomcat', 'jdk6'],
|
464
|
+
'jboss6' => ['jboss-6', 'jdk6'],
|
465
|
+
'jboss7' => ['jboss-7', 'jdk6']
|
466
|
+
}
|
467
|
+
|
468
|
+
def self.templates
|
469
|
+
return TEMPLATES
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
module Git
|
474
|
+
def self.git_repo?
|
475
|
+
`git rev-parse --show-toplevel 2> /dev/null`.strip != ""
|
476
|
+
end
|
477
|
+
|
478
|
+
def self.get_git_base_dir
|
479
|
+
`git rev-parse --show-toplevel 2> /dev/null`.strip
|
480
|
+
end
|
481
|
+
|
482
|
+
def self.add_remote(uri,remote_name)
|
483
|
+
system("git remote add #{remote_name} #{uri}")
|
484
|
+
end
|
485
|
+
|
486
|
+
def self.clone(uri,dirName,remote_name="origin")
|
487
|
+
system("git clone #{uri} #{dirName} --origin #{remote_name} --quiet")
|
488
|
+
end
|
489
|
+
|
490
|
+
def self.pull(remote_name="origin")
|
491
|
+
system("git pull #{remote_name} master --quiet")
|
492
|
+
end
|
493
|
+
|
494
|
+
def self.push(uri,remote_name)
|
495
|
+
system("git push #{remote_name} master --quiet")
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
module Debug
|
500
|
+
def self.print_post_data(h)
|
501
|
+
debug 'DEBUG: Submitting form:'
|
502
|
+
h.each do |k,v|
|
503
|
+
if k.to_s != 'password'
|
504
|
+
debug "#{k.to_s}: #{v.to_s}"
|
505
|
+
else
|
506
|
+
debug 'password: ' + ("X" * v.length)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
module Express
|
513
|
+
Maxdlen = 16
|
514
|
+
Maxretries = 10
|
515
|
+
Defaultdelay = 2
|
516
|
+
|
517
|
+
def self.delay(time, adj=Defaultdelay)
|
518
|
+
(time*=adj).to_int
|
519
|
+
end
|
520
|
+
|
521
|
+
def self.get_cartridges_list(libra_server, net_http, cart_type="standalone", debug=true, print_result=nil)
|
522
|
+
puts "Contacting https://#{libra_server} to obtain list of cartridges..."
|
523
|
+
puts " (please excuse the delay)"
|
524
|
+
data = {'cart_type' => cart_type}
|
525
|
+
if debug
|
526
|
+
data['debug'] = "true"
|
527
|
+
end
|
528
|
+
print_post_data(data, debug)
|
529
|
+
json_data = JSON.generate(data)
|
530
|
+
|
531
|
+
url = URI.parse("https://#{libra_server}/broker/cartlist")
|
532
|
+
response = http_post(net_http, url, json_data, "none")
|
533
|
+
|
534
|
+
unless response.code == '200'
|
535
|
+
print_response_err(response, debug)
|
536
|
+
return []
|
537
|
+
end
|
538
|
+
json_resp = JSON.parse(response.body)
|
539
|
+
if print_result
|
540
|
+
print_response_success(json_resp, debug)
|
541
|
+
end
|
542
|
+
begin
|
543
|
+
carts = (JSON.parse(json_resp['data']))['carts']
|
544
|
+
rescue JSON::ParserError
|
545
|
+
exit 254
|
546
|
+
end
|
547
|
+
carts
|
548
|
+
end
|
549
|
+
|
550
|
+
def self.get_cartridge_listing(carts, sep, libra_server, net_http, cart_type="standalone", debug=true, print_result=nil)
|
551
|
+
carts = get_cartridges_list(libra_server, net_http, cart_type, debug, print_result) if carts.nil?
|
552
|
+
carts.join(sep)
|
553
|
+
end
|
554
|
+
|
555
|
+
def self.get_password
|
556
|
+
password = nil
|
557
|
+
begin
|
558
|
+
print "Password: "
|
559
|
+
system "stty -echo"
|
560
|
+
password = gets.chomp
|
561
|
+
ensure
|
562
|
+
system "stty echo"
|
563
|
+
end
|
564
|
+
puts "\n"
|
565
|
+
password
|
566
|
+
end
|
567
|
+
|
568
|
+
def self.http_post(http, url, json_data, password)
|
569
|
+
req = http::Post.new(url.path)
|
570
|
+
|
571
|
+
req.set_form_data({'json_data' => json_data, 'password' => password})
|
572
|
+
http = http.new(url.host, url.port)
|
573
|
+
http.open_timeout = 10
|
574
|
+
if url.scheme == "https"
|
575
|
+
http.use_ssl = true
|
576
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
577
|
+
end
|
578
|
+
begin
|
579
|
+
response = http.start {|http| http.request(req)}
|
580
|
+
if response.code == '404' && response.content_type == 'text/html'
|
581
|
+
# TODO probably want to remove this at some point
|
582
|
+
puts "!!!! WARNING !!!! WARNING !!!! WARNING !!!!"
|
583
|
+
puts "RHCloud server not found. You might want to try updating your rhc client tools."
|
584
|
+
exit 218
|
585
|
+
end
|
586
|
+
response
|
587
|
+
rescue Exception => e
|
588
|
+
puts "There was a problem communicating with the server. Response message: #{e.message}"
|
589
|
+
puts "If you were disconnected it is possible the operation finished without being able to report success."
|
590
|
+
puts "You can use rhc-user-info and rhc-ctl-app to learn about the status of your user and application(s)."
|
591
|
+
exit 219
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
def self.print_response_err(response)
|
596
|
+
puts "Problem reported from server. Response code was #{response.code}."
|
597
|
+
exit_code = 254
|
598
|
+
if response.content_type == 'application/json'
|
599
|
+
exit_code = print_json_body(response, debug)
|
600
|
+
elsif debug
|
601
|
+
debug "HTTP response from server is #{response.body}"
|
602
|
+
end
|
603
|
+
exit exit_code.nil? ? 666 : exit_code
|
604
|
+
end
|
605
|
+
|
606
|
+
def self.print_response_messages(json_resp)
|
607
|
+
messages = json_resp['messages']
|
608
|
+
if (messages && !messages.empty?)
|
609
|
+
puts ''
|
610
|
+
puts 'MESSAGES:'
|
611
|
+
puts messages
|
612
|
+
puts ''
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
def self.print_response_success(response, debug, always_print_result=false)
|
617
|
+
if debug
|
618
|
+
puts "Response from server:"
|
619
|
+
print_json_body(response, debug)
|
620
|
+
elsif always_print_result
|
621
|
+
print_json_body(response, debug)
|
622
|
+
else
|
623
|
+
json_resp = JSON.parse(response.body)
|
624
|
+
print_response_messages(json_resp)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
def self.print_json_body(response, debug)
|
629
|
+
json_resp = JSON.parse(response.body)
|
630
|
+
print_response_messages(json_resp)
|
631
|
+
exit_code = json_resp['exit_code']
|
632
|
+
if debug
|
633
|
+
if json_resp['debug']
|
634
|
+
puts ''
|
635
|
+
puts 'DEBUG:'
|
636
|
+
puts json_resp['debug']
|
637
|
+
puts ''
|
638
|
+
puts "Exit Code: #{exit_code}"
|
639
|
+
if (json_resp.length > 3)
|
640
|
+
json_resp.each do |k,v|
|
641
|
+
if (k != 'result' && k != 'debug' && k != 'exit_code' && k != 'messages')
|
642
|
+
puts "#{k.to_s}: #{v.to_s}"
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
648
|
+
if json_resp['result']
|
649
|
+
puts ''
|
650
|
+
puts 'RESULT:'
|
651
|
+
puts json_resp['result']
|
652
|
+
puts ''
|
653
|
+
end
|
654
|
+
exit_code
|
655
|
+
end
|
656
|
+
|
657
|
+
#
|
658
|
+
# Check if host exists
|
659
|
+
#
|
660
|
+
def self.hostexist?(host)
|
661
|
+
dns = Resolv::DNS.new
|
662
|
+
resp = dns.getresources(host, Resolv::DNS::Resource::IN::A)
|
663
|
+
return resp.any?
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|