rightscale-cli 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -1
- data/bin/rightscale +25 -0
- data/bin/rs +2 -3
- data/lib/ask_pass.rb +2 -2
- data/lib/deployment_cat.rb +378 -0
- data/lib/rightscale_cli.rb +0 -1
- data/lib/rightscale_cli/client.rb +26 -16
- data/lib/rightscale_cli/clouds.rb +22 -22
- data/lib/rightscale_cli/config.rb +16 -11
- data/lib/rightscale_cli/configure.rb +24 -1
- data/lib/rightscale_cli/deployments.rb +37 -13
- data/lib/rightscale_cli/instances.rb +26 -3
- data/lib/rightscale_cli/logger.rb +8 -6
- data/lib/rightscale_cli/monkey_patches/client_attributes.rb +1 -0
- data/lib/rightscale_cli/network/networks.rb +61 -0
- data/lib/rightscale_cli/network/security_groups.rb +155 -0
- data/lib/rightscale_cli/output.rb +5 -4
- data/lib/rightscale_cli/recurring_volume_attachments.rb +50 -0
- data/lib/rightscale_cli/refresh.rb +63 -0
- data/lib/rightscale_cli/render_options.rb +12 -2
- data/lib/rightscale_cli/rightscale_cli.rb +10 -0
- data/lib/rightscale_cli/server_templates.rb +1 -1
- data/lib/rightscale_cli/servers.rb +5 -0
- data/lib/rightscale_cli/ssh_keys.rb +87 -0
- data/lib/rightscale_cli/tags.rb +29 -26
- data/lib/rightscale_cli/version.rb +2 -1
- data/lib/rightscale_cli/volumes.rb +54 -19
- data/lib/templates/right_api_client.yml.erb +6 -2
- data/lib/yesno.rb +4 -8
- metadata +31 -24
data/lib/rightscale_cli.rb
CHANGED
@@ -20,55 +20,65 @@ require 'rightscale_cli/logger'
|
|
20
20
|
require 'ask_pass'
|
21
21
|
|
22
22
|
class RightScaleCLI
|
23
|
+
# Represents a RightScale CLI Client
|
23
24
|
class Client
|
24
25
|
attr_accessor :client
|
25
26
|
attr_accessor :render
|
26
|
-
|
27
|
+
|
27
28
|
def initialize(options)
|
28
29
|
config = RightScaleCLI::Config.new.directives
|
29
30
|
config[:account_id] = options['account'] if options[:account]
|
30
31
|
config[:email] = options['user'] if options[:user]
|
31
32
|
config[:api_version] = options['api'] if options[:api]
|
33
|
+
config[:timeout] = nil
|
32
34
|
|
33
|
-
if options['password'] ||
|
35
|
+
if options['password'] || \
|
36
|
+
(!config[:password] && \
|
37
|
+
!config[:password_base64] &&
|
38
|
+
!config[:access_token])
|
39
|
+
# fallback to asking for user password
|
34
40
|
config[:password] = ask_pass
|
35
|
-
|
41
|
+
# set this to nil so it is not used by precedence
|
42
|
+
config[:password_base64] = nil
|
36
43
|
end
|
37
44
|
|
38
45
|
@options = options
|
39
46
|
@client = RightApi::Client.new(config)
|
40
|
-
@logger = RightScaleCLI::Logger.new
|
47
|
+
@logger = RightScaleCLI::Logger.new
|
41
48
|
end
|
42
49
|
|
43
50
|
def get(resource)
|
44
51
|
result = []
|
45
|
-
@client.send(resource).index.each { |record|
|
46
|
-
|
47
|
-
}
|
48
|
-
return result
|
52
|
+
@client.send(resource).index.each { |record| result.push(record.raw) }
|
53
|
+
result
|
49
54
|
end
|
50
55
|
|
51
56
|
def show(resource, resource_id, *args)
|
52
57
|
if args.count > 0
|
53
58
|
result = []
|
54
|
-
records = @client.send(resource).index(
|
55
|
-
|
56
|
-
|
57
|
-
}
|
59
|
+
records = @client.send(resource).index(id: resource_id)
|
60
|
+
.show.send(args[0]).index
|
61
|
+
records.each { |record| result.push(record.raw) }
|
58
62
|
@logger.info("Resource count: #{result.count}.")
|
59
63
|
else
|
60
|
-
result = @client.send(resource).index(
|
64
|
+
result = @client.send(resource).index(id: resource_id).show.raw
|
61
65
|
end
|
62
|
-
|
66
|
+
result
|
63
67
|
end
|
64
68
|
|
65
69
|
def create(resource, params)
|
66
|
-
|
70
|
+
if params[:cloud]
|
71
|
+
resource = @client.clouds(id: params[:cloud]).show.send("#{resource}s")
|
72
|
+
.create(params)
|
73
|
+
else
|
74
|
+
resource = @client.send("#{resource}s").create(params)
|
75
|
+
end
|
67
76
|
@logger.info("Created #{resource.href}.")
|
77
|
+
resource.href
|
68
78
|
end
|
69
79
|
|
70
80
|
def destroy(resource, resource_id)
|
71
|
-
resource = @client.send("#{resource}s").index(
|
81
|
+
resource = @client.send("#{resource}s").index(id: resource_id)
|
72
82
|
resource.destroy
|
73
83
|
@logger.info("Deleted #{resource.href}.")
|
74
84
|
end
|
@@ -19,66 +19,66 @@ require 'rightscale_cli/client'
|
|
19
19
|
require 'rightscale_cli/logger'
|
20
20
|
|
21
21
|
class RightScaleCLI
|
22
|
+
# Represents Clouds
|
22
23
|
class Clouds < Thor
|
23
24
|
namespace :clouds
|
24
25
|
|
25
26
|
def initialize(*args)
|
26
27
|
super
|
27
28
|
@client = RightScaleCLI::Client.new(options)
|
28
|
-
@logger = RightScaleCLI::Logger.new
|
29
|
+
@logger = RightScaleCLI::Logger.new
|
29
30
|
end
|
30
31
|
|
31
32
|
# include render options
|
32
33
|
eval(IO.read("#{File.dirname(File.expand_path(__FILE__))}/render_options.rb"), binding)
|
33
34
|
|
34
|
-
desc
|
35
|
-
option :basic, :
|
36
|
-
def list
|
35
|
+
desc 'list', 'lists all clouds connected to the account'
|
36
|
+
option :basic, type: :boolean, required: false
|
37
|
+
def list
|
37
38
|
clouds = @client.get('clouds')
|
38
39
|
|
39
40
|
if options[:basic]
|
40
|
-
basic_fields =
|
41
|
+
basic_fields = %w('display_name', 'cloud_type', 'name')
|
41
42
|
basic_clouds = []
|
42
|
-
@client.get('clouds').each
|
43
|
-
cloud_id = cloud['links'].select { |link| link['rel'] == 'instances' }
|
44
|
-
|
43
|
+
@client.get('clouds').each do |cloud|
|
44
|
+
cloud_id = cloud['links'].select { |link| link['rel'] == 'instances' }
|
45
|
+
.first['href'].split('/')[3]
|
46
|
+
c = cloud.select { |x| basic_fields.include?(x) }
|
45
47
|
c['cloud_id'] = cloud_id.to_i
|
46
48
|
basic_clouds.push(c)
|
47
|
-
|
49
|
+
end
|
48
50
|
clouds = basic_clouds
|
49
51
|
end
|
50
52
|
|
51
53
|
@client.render(clouds, 'clouds')
|
52
54
|
end
|
53
55
|
|
54
|
-
desc
|
56
|
+
desc 'show', 'shows a cloud'
|
55
57
|
def show(cloud_id)
|
56
58
|
@client.render(@client.show('clouds', cloud_id), 'cloud')
|
57
59
|
end
|
58
60
|
|
59
|
-
desc
|
60
|
-
option :name, :
|
61
|
-
option :cloud_type, :
|
62
|
-
option :description, :
|
63
|
-
def search
|
61
|
+
desc 'search', 'searches for clouds by cloud type, name, description'
|
62
|
+
option :name, type: :string, required: false
|
63
|
+
option :cloud_type, type: :string, required: false
|
64
|
+
option :description, type: :string, required: false
|
65
|
+
def search
|
64
66
|
filter = []
|
65
67
|
filter.push("name==#{options[:name]}") if options[:name]
|
66
68
|
filter.push("cloud_type==#{options[:cloud_type]}") if options[:cloud_type]
|
67
69
|
filter.push("description==#{options[:cloud]}") if options[:description]
|
68
70
|
|
69
|
-
@logger.info
|
71
|
+
@logger.info 'Searching for clouds!'
|
70
72
|
|
71
73
|
puts "filter: #{filter}" if options[:debug]
|
72
|
-
|
73
|
-
clouds = []
|
74
|
-
@client.client.clouds.index(:filter => filter).each { |cloud|
|
75
|
-
clouds.push(cloud.raw)
|
76
|
-
}
|
77
74
|
|
75
|
+
clouds = []
|
76
|
+
@client.client.clouds.index(filter: filter)
|
77
|
+
.each { |cloud| clouds.push(cloud.raw) }
|
78
78
|
@client.render(clouds, 'clouds')
|
79
79
|
end
|
80
80
|
|
81
|
-
def self.banner(task,
|
81
|
+
def self.banner(task, _namespace = true, subcommand = false)
|
82
82
|
"#{basename} #{task.formatted_usage(self, true, subcommand)}"
|
83
83
|
end
|
84
84
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Chris Fordham (<chris@fordham-nagy.id.au>)
|
2
|
-
# Copyright:: Copyright (c) 2013 Chris Fordham
|
2
|
+
# Copyright:: Copyright (c) 2013-2105 Chris Fordham
|
3
3
|
# License:: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -18,27 +18,32 @@ require 'yaml'
|
|
18
18
|
require 'fileutils'
|
19
19
|
|
20
20
|
class RightScaleCLI
|
21
|
+
# Represents a RightScale CLI configuration
|
21
22
|
class Config
|
22
23
|
attr_accessor :template_path, :config_home, :config_path, :directives
|
23
24
|
|
24
|
-
def initialize(*
|
25
|
-
@template_path = File.join(File.dirname(__FILE__),
|
25
|
+
def initialize(*)
|
26
|
+
@template_path = File.join(File.dirname(__FILE__),
|
27
|
+
'..', 'templates', 'right_api_client.yml.erb')
|
26
28
|
@config_home = File.join(ENV['HOME'], '.rightscale')
|
27
29
|
@config_path = File.join(@config_home, 'right_api_client.yml')
|
28
30
|
|
29
|
-
Dir.mkdir(@config_home) unless File.
|
31
|
+
Dir.mkdir(@config_home) unless File.exist?(@config_home)
|
30
32
|
FileUtils.touch(@config_path)
|
31
|
-
|
33
|
+
|
32
34
|
# write a fresh file if it does not load/parse
|
33
35
|
unless YAML.load_file(@config_path)
|
34
36
|
@directives = {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
account_id: '',
|
38
|
+
email: nil,
|
39
|
+
password_base64: '',
|
40
|
+
access_token: '',
|
41
|
+
api_url: 'https://us-4.rightscale.com',
|
42
|
+
api_version: '1.5'
|
40
43
|
}
|
41
|
-
File.open(@config_path, 'w')
|
44
|
+
File.open(@config_path, 'w') do |f|
|
45
|
+
f.write(ERB.new(IO.read(@template_path)).result(binding))
|
46
|
+
end
|
42
47
|
end
|
43
48
|
|
44
49
|
# load/reload the directives from the file
|
@@ -20,6 +20,7 @@ require 'rightscale_cli/config'
|
|
20
20
|
require 'rightscale_cli/logger'
|
21
21
|
require 'rightscale_cli/client'
|
22
22
|
require 'ask_pass'
|
23
|
+
require 'yesno'
|
23
24
|
require 'erb'
|
24
25
|
require "base64"
|
25
26
|
|
@@ -58,6 +59,18 @@ class RightScaleCLI
|
|
58
59
|
update_conf
|
59
60
|
end
|
60
61
|
|
62
|
+
desc "oauth", "Configure the RightScale OAuth access token for the client."
|
63
|
+
def oauth()
|
64
|
+
@directives.merge!( { :access_token => ask("RightScale API access token:") })
|
65
|
+
update_conf
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'refresh', 'configure the RightScale refresh token for the client'
|
69
|
+
def refresh()
|
70
|
+
@directives.merge!( { :refresh_token => ask('RightScale API refresh token:') })
|
71
|
+
update_conf
|
72
|
+
end
|
73
|
+
|
61
74
|
desc "api", "Configure the RightScale API version used by the client."
|
62
75
|
def api()
|
63
76
|
@directives.merge!( { :api_version => ask("RightScale API version (e.g. 1.5):") })
|
@@ -66,7 +79,8 @@ class RightScaleCLI
|
|
66
79
|
|
67
80
|
desc "shard", "Configure the RightScale shard used by the client."
|
68
81
|
def shard()
|
69
|
-
|
82
|
+
shard = ask("RightScale shard (e.g. us-4.rightscale.com):")
|
83
|
+
@directives.merge!( { :api_url => "https://#{shard}", :token_endpoint => "https://#{shard}/api/oauth2" })
|
70
84
|
update_conf
|
71
85
|
end
|
72
86
|
|
@@ -81,9 +95,18 @@ class RightScaleCLI
|
|
81
95
|
account
|
82
96
|
user
|
83
97
|
password
|
98
|
+
# do not ask for the api access token as we'll generate this from the refresh token
|
99
|
+
# oauth
|
100
|
+
refresh
|
84
101
|
shard
|
85
102
|
api
|
86
103
|
puts 'Configuration saved.'
|
104
|
+
if @directives[:refresh_token]
|
105
|
+
puts 'Refresh API token now? (y/n):'
|
106
|
+
if yesno
|
107
|
+
system 'rs refresh token'
|
108
|
+
end
|
109
|
+
end
|
87
110
|
end
|
88
111
|
|
89
112
|
#default_task :all
|
@@ -18,44 +18,61 @@ require 'thor'
|
|
18
18
|
require 'yaml'
|
19
19
|
require 'rightscale_cli/client'
|
20
20
|
require 'rightscale_cli/logger'
|
21
|
+
require 'rightscale_cli/config'
|
22
|
+
require 'deployment_cat'
|
23
|
+
require 'right_api_client'
|
21
24
|
|
22
25
|
class RightScaleCLI
|
26
|
+
# Represents RightScale Deployments
|
23
27
|
class Deployments < Thor
|
24
28
|
namespace :deployments
|
25
29
|
|
26
30
|
def initialize(*args)
|
27
31
|
super
|
28
32
|
@client = RightScaleCLI::Client.new(options)
|
29
|
-
@logger = RightScaleCLI::Logger.new
|
33
|
+
@logger = RightScaleCLI::Logger.new
|
34
|
+
@config = RightScaleCLI::Config.new.directives
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
37
|
+
class_option :xml,
|
38
|
+
type: :boolean,
|
39
|
+
default: false,
|
40
|
+
aliases: '-X',
|
41
|
+
required: false,
|
42
|
+
desc: 'returns xml'
|
43
|
+
class_option :json,
|
44
|
+
type: :boolean,
|
45
|
+
default: false,
|
46
|
+
aliases: '-J',
|
47
|
+
required: false,
|
48
|
+
desc: 'returns json'
|
34
49
|
|
35
|
-
desc
|
36
|
-
def list
|
50
|
+
desc 'list', 'Lists all deployments'
|
51
|
+
def list
|
37
52
|
@client.render(@client.get('deployments'), 'deployments')
|
38
53
|
end
|
39
54
|
|
40
|
-
desc
|
55
|
+
desc 'create', 'Creates a deployment'
|
41
56
|
def create(name, description)
|
42
|
-
@client.create('deployment',
|
57
|
+
@client.create('deployment', name: name, description: description)
|
43
58
|
end
|
44
59
|
|
45
|
-
desc
|
60
|
+
desc 'destroy', 'Deletes a deployment'
|
46
61
|
def destroy(deployment_id)
|
47
62
|
@client.destroy('deployment', deployment_id)
|
48
63
|
end
|
49
64
|
|
50
|
-
desc
|
65
|
+
desc 'servers', 'Lists servers in a deployment'
|
51
66
|
def servers(deployment)
|
52
67
|
@logger.info("Retrieving all servers in deployment, #{deployment}...")
|
53
|
-
@client.render(@client.show('deployments', deployment, 'servers'),
|
68
|
+
@client.render(@client.show('deployments', deployment, 'servers'),
|
69
|
+
'servers')
|
54
70
|
end
|
55
71
|
|
56
|
-
desc
|
72
|
+
desc 'terminate', 'Terminates all servers in a deployment'
|
57
73
|
def terminate(deployment_id)
|
58
|
-
@client.client.deployments.index(:
|
74
|
+
@client.client.deployments.index(id: deployment_id)
|
75
|
+
.show.servers.index.each do |server|
|
59
76
|
unless server.state == 'inactive'
|
60
77
|
@logger.info "Terminating #{server.href} (state: #{server.state}.)"
|
61
78
|
server.terminate
|
@@ -63,7 +80,14 @@ class RightScaleCLI
|
|
63
80
|
end
|
64
81
|
end
|
65
82
|
|
66
|
-
|
83
|
+
desc 'export', 'Exports a deployment into a CAT file'
|
84
|
+
def export(deployment_id)
|
85
|
+
@config[:timeout] = nil
|
86
|
+
@client = RightApi::Client.new(@config)
|
87
|
+
deployment_to_cat_file @client, deployment_id, nil, nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.banner(task, _namespace = true, subcommand = false)
|
67
91
|
"#{basename} #{task.formatted_usage(self, true, subcommand)}"
|
68
92
|
end
|
69
93
|
end
|
@@ -22,6 +22,17 @@ class RightScaleCLI
|
|
22
22
|
class Instances < Thor
|
23
23
|
namespace :instances
|
24
24
|
|
25
|
+
def initialize(*args)
|
26
|
+
super
|
27
|
+
@client = RightScaleCLI::Client.new(options)
|
28
|
+
@logger = RightScaleCLI::Logger.new()
|
29
|
+
end
|
30
|
+
|
31
|
+
# include render options
|
32
|
+
eval(IO.read(
|
33
|
+
"#{File.dirname(File.expand_path(__FILE__))}/render_options.rb"), binding
|
34
|
+
)
|
35
|
+
|
25
36
|
desc "list", "Lists all instances for a given cloud."
|
26
37
|
option :cloud, :desc => 'The cloud to filter on.', :type => :string, :required => true
|
27
38
|
option :datacenter, :desc => 'The href of the datacenter to filter on', :type => :string, :required => false
|
@@ -37,9 +48,7 @@ class RightScaleCLI
|
|
37
48
|
option :server_template, :desc => 'The href of the ServerTemplate to filter on.', :type => :string, :required => false
|
38
49
|
option :state, :desc => 'The state of Instances to filter on.', :type => :string, :required => false
|
39
50
|
def list()
|
40
|
-
instances = []
|
41
51
|
filter = []
|
42
|
-
|
43
52
|
filter.push("datacenter_href==#{options[:datacenter]}") if options[:datacenter]
|
44
53
|
filter.push("deployment_href==#{options[:deployment]}") if options[:deployment]
|
45
54
|
filter.push("name==#{options[:private_ip]}") if options[:name]
|
@@ -55,7 +64,21 @@ class RightScaleCLI
|
|
55
64
|
|
56
65
|
@logger.debug "filter: #{filter}" if options[:debug]
|
57
66
|
|
58
|
-
|
67
|
+
instances = []
|
68
|
+
@client.client.clouds(:id => options[:cloud]).show.instances(:filter => filter).index.each do |instance|
|
69
|
+
instance_href = instance.href
|
70
|
+
instance = instance.raw
|
71
|
+
instance['href'] = instance_href
|
72
|
+
instances.push(instance)
|
73
|
+
end
|
74
|
+
@client.render(instances, 'instances')
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "show", "Shows attributes of a single instance."
|
78
|
+
option :cloud, :desc => 'The cloud to filter on.', :type => :string, :required => true
|
79
|
+
def show(instance_id)
|
80
|
+
filter = []
|
81
|
+
@client.render(@client.client.clouds(:id => options[:cloud]).show.instances.index(:id => instance_id).show.raw, 'instance')
|
59
82
|
end
|
60
83
|
|
61
84
|
desc "run-exec", "Runs a chef recipe or rightscript on instances of a given cloud."
|
@@ -18,15 +18,17 @@ require 'logger'
|
|
18
18
|
require 'rightscale_cli/config'
|
19
19
|
|
20
20
|
class RightScaleCLI
|
21
|
+
# Represents a RightScale CLI Logger
|
21
22
|
class Logger
|
22
23
|
attr_accessor :log
|
23
|
-
|
24
|
-
def initialize(*
|
24
|
+
|
25
|
+
def initialize(*)
|
25
26
|
@log_init_msg = 'Initializing Logging using '
|
26
27
|
|
27
28
|
if ENV['RIGHT_API_CLIENT_LOG']
|
28
|
-
if File.
|
29
|
-
file = File.open(ENV['RIGHT_API_CLIENT_LOG'],
|
29
|
+
if File.exist?(ENV['RIGHT_API_CLIENT_LOG'])
|
30
|
+
file = File.open(ENV['RIGHT_API_CLIENT_LOG'],
|
31
|
+
File::WRONLY | File::APPEND)
|
30
32
|
else
|
31
33
|
file = ENV['RIGHT_API_CLIENT_LOG']
|
32
34
|
end
|
@@ -38,14 +40,14 @@ class RightScaleCLI
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
def init_message
|
43
|
+
def init_message
|
42
44
|
@log.info @log_init_msg
|
43
45
|
end
|
44
46
|
|
45
47
|
def info(msg)
|
46
48
|
@log.info msg
|
47
49
|
end
|
48
|
-
|
50
|
+
|
49
51
|
def debug(msg)
|
50
52
|
@log.debug msg
|
51
53
|
end
|