morpheus-cli 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/bin/morpheus +27 -32
  3. data/lib/morpheus/api/accounts_interface.rb +36 -47
  4. data/lib/morpheus/api/api_client.rb +141 -110
  5. data/lib/morpheus/api/app_templates_interface.rb +56 -72
  6. data/lib/morpheus/api/apps_interface.rb +111 -132
  7. data/lib/morpheus/api/auth_interface.rb +30 -0
  8. data/lib/morpheus/api/clouds_interface.rb +71 -76
  9. data/lib/morpheus/api/custom_instance_types_interface.rb +21 -46
  10. data/lib/morpheus/api/dashboard_interface.rb +10 -17
  11. data/lib/morpheus/api/deploy_interface.rb +60 -72
  12. data/lib/morpheus/api/deployments_interface.rb +53 -71
  13. data/lib/morpheus/api/groups_interface.rb +55 -45
  14. data/lib/morpheus/api/instance_types_interface.rb +19 -23
  15. data/lib/morpheus/api/instances_interface.rb +179 -177
  16. data/lib/morpheus/api/key_pairs_interface.rb +11 -17
  17. data/lib/morpheus/api/license_interface.rb +18 -23
  18. data/lib/morpheus/api/load_balancers_interface.rb +54 -69
  19. data/lib/morpheus/api/logs_interface.rb +25 -29
  20. data/lib/morpheus/api/options_interface.rb +13 -17
  21. data/lib/morpheus/api/provision_types_interface.rb +19 -22
  22. data/lib/morpheus/api/roles_interface.rb +75 -94
  23. data/lib/morpheus/api/security_group_rules_interface.rb +28 -37
  24. data/lib/morpheus/api/security_groups_interface.rb +39 -51
  25. data/lib/morpheus/api/servers_interface.rb +113 -115
  26. data/lib/morpheus/api/setup_interface.rb +31 -0
  27. data/lib/morpheus/api/task_sets_interface.rb +36 -38
  28. data/lib/morpheus/api/tasks_interface.rb +56 -69
  29. data/lib/morpheus/api/users_interface.rb +67 -76
  30. data/lib/morpheus/api/virtual_images_interface.rb +61 -61
  31. data/lib/morpheus/api/whoami_interface.rb +12 -15
  32. data/lib/morpheus/cli.rb +71 -60
  33. data/lib/morpheus/cli/accounts.rb +254 -315
  34. data/lib/morpheus/cli/alias_command.rb +219 -0
  35. data/lib/morpheus/cli/app_templates.rb +264 -272
  36. data/lib/morpheus/cli/apps.rb +608 -671
  37. data/lib/morpheus/cli/cli_command.rb +259 -21
  38. data/lib/morpheus/cli/cli_registry.rb +99 -14
  39. data/lib/morpheus/cli/clouds.rb +599 -372
  40. data/lib/morpheus/cli/config_file.rb +126 -0
  41. data/lib/morpheus/cli/credentials.rb +141 -117
  42. data/lib/morpheus/cli/dashboard_command.rb +48 -56
  43. data/lib/morpheus/cli/deployments.rb +254 -268
  44. data/lib/morpheus/cli/deploys.rb +150 -142
  45. data/lib/morpheus/cli/error_handler.rb +38 -0
  46. data/lib/morpheus/cli/groups.rb +551 -179
  47. data/lib/morpheus/cli/hosts.rb +862 -617
  48. data/lib/morpheus/cli/instance_types.rb +103 -95
  49. data/lib/morpheus/cli/instances.rb +1335 -1009
  50. data/lib/morpheus/cli/key_pairs.rb +82 -90
  51. data/lib/morpheus/cli/library.rb +498 -499
  52. data/lib/morpheus/cli/license.rb +83 -101
  53. data/lib/morpheus/cli/load_balancers.rb +314 -300
  54. data/lib/morpheus/cli/login.rb +66 -44
  55. data/lib/morpheus/cli/logout.rb +47 -46
  56. data/lib/morpheus/cli/mixins/accounts_helper.rb +69 -31
  57. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +106 -0
  58. data/lib/morpheus/cli/mixins/print_helper.rb +181 -17
  59. data/lib/morpheus/cli/mixins/provisioning_helper.rb +535 -458
  60. data/lib/morpheus/cli/mixins/whoami_helper.rb +2 -2
  61. data/lib/morpheus/cli/option_parser.rb +35 -0
  62. data/lib/morpheus/cli/option_types.rb +232 -192
  63. data/lib/morpheus/cli/recent_activity_command.rb +61 -65
  64. data/lib/morpheus/cli/remote.rb +446 -199
  65. data/lib/morpheus/cli/roles.rb +884 -906
  66. data/lib/morpheus/cli/security_group_rules.rb +213 -203
  67. data/lib/morpheus/cli/security_groups.rb +237 -192
  68. data/lib/morpheus/cli/shell.rb +338 -231
  69. data/lib/morpheus/cli/tasks.rb +326 -308
  70. data/lib/morpheus/cli/users.rb +457 -462
  71. data/lib/morpheus/cli/version.rb +1 -1
  72. data/lib/morpheus/cli/version_command.rb +16 -18
  73. data/lib/morpheus/cli/virtual_images.rb +526 -345
  74. data/lib/morpheus/cli/whoami.rb +125 -111
  75. data/lib/morpheus/cli/workflows.rb +338 -185
  76. data/lib/morpheus/formatters.rb +8 -1
  77. data/lib/morpheus/logging.rb +1 -1
  78. data/lib/morpheus/rest_client.rb +17 -8
  79. metadata +9 -3
  80. data/lib/morpheus/api/custom_instance_types.rb +0 -55
@@ -9,146 +9,154 @@ require 'morpheus/cli/cli_command'
9
9
  class Morpheus::Cli::Deploys
10
10
  include Morpheus::Cli::CliCommand
11
11
 
12
- cli_command_name :deploy
13
-
14
- def initialize()
15
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
16
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
17
- @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
18
- @deploy_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).deploy
19
- end
20
-
21
- def handle(args)
22
- if @access_token.empty?
23
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
24
- return 1
25
- end
26
-
27
- deploy(args)
28
- end
29
-
30
- def deploy(args)
31
- environment = 'production'
32
- if args.count > 0
33
- environment = args[0]
34
- end
35
- if load_deploy_file().nil?
36
- puts "Morpheus Deploy File `morpheus.yml` not detected. Please create one and try again."
37
- return
38
- end
39
-
40
- deploy_args = merged_deploy_args(environment)
41
- if deploy_args['name'].nil?
42
- puts "Instance not specified. Please specify the instance name and try again."
43
- return
44
- end
45
-
46
- instance_results = @instances_interface.get(name: deploy_args['name'])
47
- if instance_results['instances'].empty?
48
- puts "Instance not found by name #{args[0]}"
49
- return
50
- end
51
- instance = instance_results['instances'][0]
52
- instance_id = instance['id']
53
- print "\n" ,cyan, bold, "Morpheus Deployment\n","==================", reset, "\n\n"
54
-
55
- if !deploy_args['script'].nil?
56
- print cyan, bold, " - Executing Pre Deploy Script...", reset, "\n"
57
-
58
- if !system(deploy_args['script'])
59
- puts "Error executing pre script..."
60
- return
61
- end
62
- end
63
- # Create a new deployment record
64
- deploy_result = @deploy_interface.create(instance_id)
65
- app_deploy = deploy_result['appDeploy']
66
- deployment_id = app_deploy['id']
67
-
68
- # Upload Files
69
- print "\n",cyan, bold, "Uploading Files...", reset, "\n"
70
- current_working_dir = Dir.pwd
71
- deploy_args['files'].each do |fmap|
72
- Dir.chdir(fmap['path'] || current_working_dir)
73
- files = Dir.glob(fmap['pattern'] || '**/*')
74
- files.each do |file|
75
- if File.file?(file)
76
- print cyan,bold, " - Uploading #{file} ...", reset, "\n"
77
- destination = file.split("/")[0..-2].join("/")
78
- @deploy_interface.upload_file(deployment_id,file,destination)
79
- end
80
- end
81
- end
82
- print cyan, bold, "Upload Complete!", reset, "\n"
83
- Dir.chdir(current_working_dir)
84
-
85
- if !deploy_args['post_script'].nil?
86
- print cyan, bold, "Executing Post Script...", reset, "\n"
87
- if !system(deploy_args['post_script'])
88
- puts "Error executing post script..."
89
- return
90
- end
91
- end
92
-
93
- deploy_payload = {}
94
- if deploy_args['env']
95
- evars = []
96
- deploy_args['env'].each_pair do |key, value|
97
- evars << {name: key, value: value, export: false}
98
- end
99
- @instances_interface.create_env(instance_id,evars)
100
- @instances_interface.restart(instance_id)
101
- end
102
- if deploy_args['options']
103
- deploy_payload = {
104
- appDeploy: {
105
- config: deploy_args['options']
106
- }
107
- }
108
- end
109
-
110
- print cyan, bold, "Deploying to Servers...", reset, "\n"
111
- @deploy_interface.deploy(deployment_id,deploy_payload)
112
- print cyan, bold, "Deploy Successful!", reset, "\n"
113
- end
114
-
115
- def list(args)
116
- end
117
-
118
- def rollback(args)
119
- end
120
-
121
- # Loads a morpheus.yml file from within the current working directory.
122
- # This file contains information necessary in the project to perform a deployment via the cli
123
- #
124
- # === Example File Attributes
125
- # * +script+ - The initial script to run before uploading files
126
- # * +name+ - The instance name we are deploying to (can be overridden in CLI)
127
- # * +remote+ - Optional remote appliance name we are connecting to
128
- # * +files+ - List of file patterns to use for uploading files and their target destination
129
- # * +options+ - Map of deployment options depending on deployment type
130
- # * +post_script+ - A post operation script to be run on the local machine
131
- # * +stage_deploy+ - If set to true the deploy will only be staged and not actually run
132
- #
133
- # +NOTE: + It is also possible to nest these properties in an "environments" map to override based on a passed environment deploy name
134
- #
135
- def load_deploy_file
136
- if !File.exist? "morpheus.yml"
137
- puts "No morpheus.yml file detected in the current directory. Nothing to do."
138
- return nil
139
- end
140
-
141
- @deploy_file = YAML.load_file("morpheus.yml")
142
- return @deploy_file
143
- end
144
-
145
- def merged_deploy_args(environment)
146
- environment = environment || production
147
-
148
- deploy_args = @deploy_file.reject { |key,value| key == 'environment'}
149
- if !@deploy_file['environment'].nil? && !@deploy_file['environment'][environment].nil?
150
- deploy_args = deploy_args.merge(@deploy_file['environment'][environment])
151
- end
152
- return deploy_args
153
- end
12
+ set_command_name :deploy
13
+
14
+ def initialize()
15
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
16
+ end
17
+
18
+ def connect(opts)
19
+ @api_client = establish_remote_appliance_connection(opts)
20
+ @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
21
+ @deploy_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).deploy
22
+ end
23
+
24
+ def handle(args)
25
+ deploy(args)
26
+ end
27
+
28
+ def deploy(args)
29
+ options={}
30
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
31
+ opts.banner = "Usage: morpheus deploy [environment]"
32
+ build_common_options(opts, options, [])
33
+ opts.footer = "Deploy to an environment using the morpheus.yml file, located in the working directory."
34
+ # "todo: document me better!"
35
+ end
36
+ optparse.parse!(args)
37
+ connect(options)
38
+ environment = 'production' # yikes!
39
+ if args.count > 0
40
+ environment = args[0]
41
+ end
42
+ if load_deploy_file().nil?
43
+ puts "Morpheus Deploy File `morpheus.yml` not detected. Please create one and try again."
44
+ return
45
+ end
46
+
47
+ deploy_args = merged_deploy_args(environment)
48
+ if deploy_args['name'].nil?
49
+ puts "Instance not specified. Please specify the instance name and try again."
50
+ return
51
+ end
52
+
53
+ instance_results = @instances_interface.get(name: deploy_args['name'])
54
+ if instance_results['instances'].empty?
55
+ puts "Instance not found by name #{args[0]}"
56
+ return
57
+ end
58
+ instance = instance_results['instances'][0]
59
+ instance_id = instance['id']
60
+ print "\n" ,cyan, bold, "Morpheus Deployment\n","==================", reset, "\n\n"
61
+
62
+ if !deploy_args['script'].nil?
63
+ print cyan, bold, " - Executing Pre Deploy Script...", reset, "\n"
64
+
65
+ if !system(deploy_args['script'])
66
+ puts "Error executing pre script..."
67
+ return
68
+ end
69
+ end
70
+ # Create a new deployment record
71
+ deploy_result = @deploy_interface.create(instance_id)
72
+ app_deploy = deploy_result['appDeploy']
73
+ deployment_id = app_deploy['id']
74
+
75
+ # Upload Files
76
+ print "\n",cyan, bold, "Uploading Files...", reset, "\n"
77
+ current_working_dir = Dir.pwd
78
+ deploy_args['files'].each do |fmap|
79
+ Dir.chdir(fmap['path'] || current_working_dir)
80
+ files = Dir.glob(fmap['pattern'] || '**/*')
81
+ files.each do |file|
82
+ if File.file?(file)
83
+ print cyan,bold, " - Uploading #{file} ...", reset, "\n"
84
+ destination = file.split("/")[0..-2].join("/")
85
+ @deploy_interface.upload_file(deployment_id,file,destination)
86
+ end
87
+ end
88
+ end
89
+ print cyan, bold, "Upload Complete!", reset, "\n"
90
+ Dir.chdir(current_working_dir)
91
+
92
+ if !deploy_args['post_script'].nil?
93
+ print cyan, bold, "Executing Post Script...", reset, "\n"
94
+ if !system(deploy_args['post_script'])
95
+ puts "Error executing post script..."
96
+ return
97
+ end
98
+ end
99
+
100
+ deploy_payload = {}
101
+ if deploy_args['env']
102
+ evars = []
103
+ deploy_args['env'].each_pair do |key, value|
104
+ evars << {name: key, value: value, export: false}
105
+ end
106
+ payload = {envs: evars}
107
+ @instances_interface.create_env(instance_id, payload)
108
+ @instances_interface.restart(instance_id)
109
+ end
110
+ if deploy_args['options']
111
+ deploy_payload = {
112
+ appDeploy: {
113
+ config: deploy_args['options']
114
+ }
115
+ }
116
+ end
117
+
118
+ print cyan, bold, "Deploying to Servers...", reset, "\n"
119
+ @deploy_interface.deploy(deployment_id,deploy_payload)
120
+ print cyan, bold, "Deploy Successful!", reset, "\n"
121
+ end
122
+
123
+ def list(args)
124
+ end
125
+
126
+ def rollback(args)
127
+ end
128
+
129
+ # Loads a morpheus.yml file from within the current working directory.
130
+ # This file contains information necessary in the project to perform a deployment via the cli
131
+ #
132
+ # === Example File Attributes
133
+ # * +script+ - The initial script to run before uploading files
134
+ # * +name+ - The instance name we are deploying to (can be overridden in CLI)
135
+ # * +remote+ - Optional remote appliance name we are connecting to
136
+ # * +files+ - List of file patterns to use for uploading files and their target destination
137
+ # * +options+ - Map of deployment options depending on deployment type
138
+ # * +post_script+ - A post operation script to be run on the local machine
139
+ # * +stage_deploy+ - If set to true the deploy will only be staged and not actually run
140
+ #
141
+ # +NOTE: + It is also possible to nest these properties in an "environments" map to override based on a passed environment deploy name
142
+ #
143
+ def load_deploy_file
144
+ if !File.exist? "morpheus.yml"
145
+ puts "No morpheus.yml file detected in the current directory. Nothing to do."
146
+ return nil
147
+ end
148
+
149
+ @deploy_file = YAML.load_file("morpheus.yml")
150
+ return @deploy_file
151
+ end
152
+
153
+ def merged_deploy_args(environment)
154
+ environment = environment || production
155
+
156
+ deploy_args = @deploy_file.reject { |key,value| key == 'environment'}
157
+ if !@deploy_file['environment'].nil? && !@deploy_file['environment'][environment].nil?
158
+ deploy_args = deploy_args.merge(@deploy_file['environment'][environment])
159
+ end
160
+ return deploy_args
161
+ end
154
162
  end
@@ -0,0 +1,38 @@
1
+ require 'term/ansicolor'
2
+ require 'optparse'
3
+ #require 'rest_client'
4
+ require 'morpheus/logging'
5
+ class Morpheus::Cli::ErrorHandler
6
+ include Morpheus::Cli::PrintHelper
7
+
8
+ def handle_error(err, options={})
9
+ # heh
10
+ if Morpheus::Logging.debug? && options[:debug].nil?
11
+ options[:debug] = true
12
+ end
13
+ case (err)
14
+ when OptionParser::InvalidOption, OptionParser::AmbiguousOption, OptionParser::MissingArgument, OptionParser::InvalidArgument
15
+ # raise e
16
+ print_red_alert "#{err.message}"
17
+ puts "Try -h for help with this command."
18
+ when Errno::ECONNREFUSED
19
+ print_red_alert "#{err.message}"
20
+ # more special errors?
21
+ when RestClient::Exception
22
+ print_rest_exception(err, options)
23
+ else
24
+ print_red_alert "Unexpected Error."
25
+ if !options[:debug]
26
+ print "Use --debug for more information.\n"
27
+ end
28
+ end
29
+
30
+ if options[:debug]
31
+ print Term::ANSIColor.red, "\n", "#{err.class}: #{err.message}", "\n", Term::ANSIColor.reset
32
+ print err.backtrace.join("\n"), "\n\n"
33
+ else
34
+ #print "Use --debug for more information.\n"
35
+ end
36
+ end
37
+
38
+ end
@@ -4,187 +4,559 @@ require 'rest_client'
4
4
  require 'optparse'
5
5
  require 'table_print'
6
6
  require 'morpheus/cli/cli_command'
7
+ require 'morpheus/cli/mixins/infrastructure_helper'
8
+ require 'morpheus/logging'
7
9
 
8
10
  class Morpheus::Cli::Groups
9
11
  include Morpheus::Cli::CliCommand
12
+ include Morpheus::Cli::InfrastructureHelper
13
+
14
+ register_subcommands :list, :get, :add, :update, :use, :unuse, :add_cloud, :remove_cloud, :remove, :current => :print_current
15
+ alias_subcommand :details, :get
16
+ set_default_subcommand :list
17
+
18
+ def initialize()
19
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
20
+ end
21
+
22
+ def connect(opts)
23
+ @api_client = establish_remote_appliance_connection(opts)
24
+ @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
25
+ @clouds_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).clouds
26
+ @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
27
+ end
28
+
29
+ def handle(args)
30
+ handle_subcommand(args)
31
+ end
32
+
33
+ def list(args)
34
+ options = {}
35
+ params = {}
36
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
37
+ opts.banner = subcommand_usage()
38
+ build_common_options(opts, options, [:list, :json, :remote])
39
+ opts.footer = "This outputs a paginated list of groups."
40
+ end
41
+ optparse.parse!(args)
42
+ connect(options)
43
+ begin
44
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
45
+ params[k] = options[k] unless options[k].nil?
46
+ end
47
+ json_response = @groups_interface.get(params)
48
+ if options[:json]
49
+ print JSON.pretty_generate(json_response)
50
+ print "\n"
51
+ return
52
+ end
53
+ groups = json_response['groups']
54
+ print "\n" ,cyan, bold, "Morpheus Groups\n","==================", reset, "\n\n"
55
+ if groups.empty?
56
+ puts yellow,"No groups currently configured.",reset
57
+ else
58
+ print_groups_table(groups)
59
+ print_results_pagination(json_response)
60
+ if @active_group_id
61
+ active_group = groups.find { |it| it['id'] == @active_group_id }
62
+ active_group = active_group || find_group_by_name_or_id(@active_group_id)
63
+ #unless @appliances.keys.size == 1
64
+ print cyan, "\n# => Currently using #{active_group['name']}\n", reset
65
+ #end
66
+ else
67
+ unless options[:remote]
68
+ print "\n# => No active group, see `groups use`\n", reset
69
+ end
70
+ end
71
+ end
72
+ print reset,"\n"
73
+ rescue RestClient::Exception => e
74
+ print_rest_exception(e, options)
75
+ exit 1
76
+ end
77
+ end
78
+
79
+ def get(args)
80
+ options = {}
81
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
82
+ opts.banner = subcommand_usage("[name]")
83
+ build_common_options(opts, options, [:json, :remote])
84
+ opts.footer = "This outputs details about a specific group."
85
+ end
86
+ optparse.parse!(args)
87
+ if args.count < 1
88
+ puts optparse
89
+ exit 1
90
+ end
91
+ connect(options)
92
+ begin
93
+ group = find_group_by_name_or_id(args[0])
94
+ #json_response = @groups_interface.get(group['id'])
95
+ json_response = {'group' => group}
96
+ if options[:json]
97
+ print JSON.pretty_generate(json_response), "\n"
98
+ return
99
+ end
100
+ group = json_response['group']
101
+
102
+ is_active = @active_group_id && (@active_group_id == group['id'])
103
+
104
+ print "\n" ,cyan, bold, "Group Details\n","==================", reset, "\n\n"
105
+ print cyan
106
+ puts "ID: #{group['id']}"
107
+ puts "Name: #{group['name']}"
108
+ puts "Code: #{group['code']}"
109
+ puts "Location: #{group['location']}"
110
+ puts "Clouds: #{group['zones'].collect {|it| it['name'] }.join(', ')}"
111
+ puts "Hosts: #{group['serverCount']}"
112
+ if is_active
113
+ puts "\n => This is the active group."
114
+ end
115
+
116
+ print reset,"\n"
117
+
118
+ #puts instance
119
+ rescue RestClient::Exception => e
120
+ print_rest_exception(e, options)
121
+ exit 1
122
+ end
123
+ end
124
+
125
+ def add(args)
126
+ options = {}
127
+ params = {}
128
+ use_it = false
129
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
130
+ opts.banner = subcommand_usage("[name]")
131
+ opts.on( '-l', '--location LOCATION', "Location" ) do |val|
132
+ params[:location] = val
133
+ end
134
+ opts.on( '--use', '--use', "Make this the current active group" ) do
135
+ use_it = true
136
+ end
137
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
138
+ opts.footer = "Create a new group."
139
+ end
140
+ optparse.parse!(args)
141
+ # if args.count < 1
142
+ # puts optparse
143
+ # exit 1
144
+ # end
145
+ connect(options)
146
+ begin
147
+ # group = {name: args[0], location: params[:location]}
148
+ # payload = {group: group}
149
+ group_payload = {}
150
+ if args[0]
151
+ group_payload[:name] = args[0]
152
+ options[:options]['name'] = args[0] # to skip prompt
153
+ end
154
+ if params[:location]
155
+ group_payload[:name] = params[:location]
156
+ options[:options]['location'] = params[:location] # to skip prompt
157
+ end
158
+ all_option_types = add_group_option_types()
159
+ params = Morpheus::Cli::OptionTypes.prompt(all_option_types, options[:options], @api_client, {})
160
+ group_payload.merge!(params)
161
+ payload = {group: group_payload}
162
+
163
+ if options[:dry_run]
164
+ print_dry_run @groups_interface.dry.create(payload)
165
+ return
166
+ end
167
+ json_response = @groups_interface.create(payload)
168
+ group = json_response['group']
169
+ if use_it
170
+ ::Morpheus::Cli::Groups.set_active_group(@appliance_name, group['id'])
171
+ end
172
+ if options[:json]
173
+ print JSON.pretty_generate(json_response)
174
+ print "\n"
175
+ else
176
+ print_green_success "Added group #{group['name']}"
177
+ list([])
178
+ end
179
+ rescue RestClient::Exception => e
180
+ print_rest_exception(e, options)
181
+ exit 1
182
+ end
183
+ end
184
+
185
+ def update(args)
186
+ options = {}
187
+ params = {}
188
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
189
+ opts.banner = subcommand_usage("[name] [options]")
190
+ opts.on( '-l', '--location LOCATION', "Location" ) do |val|
191
+ params[:location] = val
192
+ end
193
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
194
+ opts.footer = "Update an existing group."
195
+ end
196
+ optparse.parse!(args)
197
+ if args.count < 1
198
+ puts optparse
199
+ exit 1
200
+ end
201
+ connect(options)
202
+ begin
203
+ group = find_group_by_name_or_id(args[0])
204
+ group_payload = {id: group['id']}
205
+
206
+ all_option_types = update_group_option_types()
207
+ #params = Morpheus::Cli::OptionTypes.prompt(all_option_types, options[:options], @api_client, {})
208
+ params = options[:options] || {}
209
+
210
+ if params.empty?
211
+ puts optparse.banner
212
+ print_available_options(all_option_types)
213
+ exit 1
214
+ end
215
+
216
+ group_payload.merge!(params)
217
+
218
+ payload = {group: group_payload}
219
+
220
+ if options[:dry_run]
221
+ print_dry_run @groups_interface.dry.update(group['id'], payload)
222
+ return
223
+ end
224
+ json_response = @groups_interface.update(group['id'], payload)
225
+ if options[:json]
226
+ print JSON.pretty_generate(json_response)
227
+ print "\n"
228
+ else
229
+ #list([])
230
+ get([group["id"]])
231
+ end
232
+ rescue RestClient::Exception => e
233
+ print_rest_exception(e, options)
234
+ exit 1
235
+ end
236
+ end
237
+
238
+ def add_cloud(args)
239
+ options = {}
240
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
241
+ opts.banner = subcommand_usage("[name]", "CLOUD")
242
+ build_common_options(opts, options, [:json, :dry_run, :remote])
243
+ opts.footer = "Add a cloud to a group."
244
+ end
245
+ optparse.parse!(args)
246
+ if args.count < 2
247
+ puts optparse
248
+ exit 1
249
+ end
250
+ connect(options)
251
+ begin
252
+ group = find_group_by_name_or_id(args[0])
253
+ cloud = find_cloud_by_name_or_id(args[1])
254
+ current_zones = group['zones']
255
+ found_zone = current_zones.find {|it| it["id"] == cloud["id"] }
256
+ if found_zone
257
+ print_red_alert "Cloud #{cloud['name']} is already in group #{group['name']}."
258
+ exit 1
259
+ end
260
+ new_zones = current_zones + [{'id' => cloud['id']}]
261
+ payload = {group: {id: group["id"], zones: new_zones}}
262
+ if options[:dry_run]
263
+ print_dry_run @groups_interface.dry.update_zones(group["id"], payload)
264
+ return
265
+ end
266
+ json_response = @groups_interface.update_zones(group["id"], payload)
267
+ if options[:json]
268
+ print JSON.pretty_generate(json_response)
269
+ print "\n"
270
+ else
271
+ print_green_success "Added cloud #{cloud["id"]} to group #{group['name']}"
272
+ #list([])
273
+ get([group["id"]])
274
+ end
275
+ rescue RestClient::Exception => e
276
+ print_rest_exception(e, options)
277
+ exit 1
278
+ end
279
+ end
280
+
281
+ def remove_cloud(args)
282
+ options = {}
283
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
284
+ opts.banner = subcommand_usage("[name]", "CLOUD")
285
+ build_common_options(opts, options, [:json, :dry_run, :remote])
286
+ opts.footer = "Remove a cloud from a group."
287
+ end
288
+ optparse.parse!(args)
289
+ if args.count < 2
290
+ puts optparse
291
+ exit 1
292
+ end
293
+ connect(options)
294
+ begin
295
+ group = find_group_by_name_or_id(args[0])
296
+ cloud = find_cloud_by_name_or_id(args[1])
297
+ current_zones = group['zones']
298
+ found_zone = current_zones.find {|it| it["id"] == cloud["id"] }
299
+ if !found_zone
300
+ print_red_alert "Cloud #{cloud['name']} is not in group #{group['name']}."
301
+ exit 1
302
+ end
303
+ new_zones = current_zones.reject {|it| it["id"] == cloud["id"] }
304
+ payload = {group: {id: group["id"], zones: new_zones}}
305
+ if options[:dry_run]
306
+ print_dry_run @groups_interface.dry.update_zones(group["id"], payload)
307
+ return
308
+ end
309
+ json_response = @groups_interface.update_zones(group["id"], payload)
310
+ if options[:json]
311
+ print JSON.pretty_generate(json_response)
312
+ print "\n"
313
+ else
314
+ print_green_success "Removed cloud #{cloud['name']} from group #{group['name']}"
315
+ # list([])
316
+ get([group["id"]])
317
+ end
318
+ rescue RestClient::Exception => e
319
+ print_rest_exception(e, options)
320
+ exit 1
321
+ end
322
+ end
323
+
324
+ def remove(args)
325
+ options = {}
326
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
327
+ opts.banner = subcommand_usage("[name]")
328
+ build_common_options(opts, options, [:json, :dry_run, :auto_confirm, :remote])
329
+ opts.footer = "Delete a group."
330
+ # more info to display here
331
+ end
332
+ optparse.parse!(args)
333
+ if args.count < 1
334
+ puts optparse
335
+ exit 1
336
+ end
337
+ connect(options)
338
+
339
+ begin
340
+ group = find_group_by_name_or_id(args[0])
341
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the group #{group['name']}?")
342
+ exit
343
+ end
344
+ if options[:dry_run]
345
+ print_dry_run @groups_interface.dry.destroy(group['id'])
346
+ return
347
+ end
348
+ json_response = @groups_interface.destroy(group['id'])
349
+ if options[:json]
350
+ print JSON.pretty_generate(json_response)
351
+ print "\n"
352
+ elsif !options[:quiet]
353
+ print_green_success "Removed group #{group['name']}"
354
+ #list([])
355
+ end
356
+ rescue RestClient::Exception => e
357
+ print_rest_exception(e, options)
358
+ exit 1
359
+ end
360
+ end
361
+
362
+ def use(args)
363
+ options = {}
364
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
365
+ opts.banner = subcommand_usage("[name]")
366
+ opts.footer = "" +
367
+ "This sets the current active group.\n" +
368
+ "It will be auto-selected for use during provisioning.\n" +
369
+ "You can still use the --group option to override this."
370
+ build_common_options(opts, options, [])
371
+ end
372
+ optparse.parse!(args)
373
+ if args.count < 1
374
+ puts optparse
375
+ exit 1
376
+ end
377
+ connect(options)
378
+ begin
379
+ # todo: this is a problem for unprivileged users, need to use find_group_by_id_for_provisioning(group_id)
380
+ group = find_group_by_name_or_id(args[0])
381
+ if !group
382
+ print_red_alert "Group not found by name #{args[0]}"
383
+ exit 1
384
+ end
385
+
386
+ if @active_group_id == group['id']
387
+ print reset,"Already using the group #{group['name']}","\n",reset
388
+ else
389
+ ::Morpheus::Cli::Groups.set_active_group(@appliance_name, group['id'])
390
+ # ::Morpheus::Cli::Groups.save_groups(@active_groups)
391
+ #print cyan,"Switched active group to #{group['name']}","\n",reset
392
+ #list([])
393
+ end
394
+ rescue RestClient::Exception => e
395
+ print_rest_exception(e, options)
396
+ exit 1
397
+ end
398
+
399
+ end
400
+
401
+ def unuse(args)
402
+ options = {}
403
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
404
+ opts.banner = subcommand_usage() + "\n\n" +
405
+ "This will clear the active group." + "\n" +
406
+ "You will be prompted for a Group during provisioning." + "\n\n"
407
+ build_common_options(opts, options, [])
408
+ end
409
+ optparse.parse!(args)
410
+ connect(options)
411
+
412
+ if @active_group_id
413
+ ::Morpheus::Cli::Groups.clear_active_group(@appliance_name)
414
+ # unless options[:quiet]
415
+ # print cyan
416
+ # puts "Switched to no active group."
417
+ # puts "You will be prompted for Group during provisioning."
418
+ # print reset
419
+ # end
420
+ return true
421
+ else
422
+ puts "You are not using any group for appliance #{@appliance_name}"
423
+ #return false
424
+ end
425
+ end
426
+
427
+ def print_current(args)
428
+ options = {}
429
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
430
+ opts.banner = subcommand_usage()
431
+ build_common_options(opts, options, [])
432
+ opts.footer = "" +
433
+ "This will set the current remote appliance.\n" +
434
+ "It will be used for all subsequent commands.\n" +
435
+ "You may still use the --remote option to override this."
436
+ end
437
+ optparse.parse!(args)
438
+ connect(options)
439
+
440
+ group = @active_group_id ? find_group_by_name_or_id(@active_group_id) : nil
441
+ if group
442
+ print cyan,group['name'].to_s,"\n",reset
443
+ else
444
+ print dark,"No active group. See `groups use`","\n",reset
445
+ end
446
+ end
447
+
448
+ protected
449
+
450
+ def print_groups_table(groups, opts={})
451
+ table_color = opts[:color] || cyan
452
+ active_group_id = @active_group_id # Morpheus::Cli::Groups.active_group
453
+ rows = groups.collect do |group|
454
+ is_active = @active_group_id && (@active_group_id == group['id'])
455
+ {
456
+ active: (is_active ? "=>" : ""),
457
+ id: group['id'],
458
+ name: group['name'],
459
+ location: group['location'],
460
+ cloud_count: group['zones'] ? group['zones'].size : 0,
461
+ server_count: group['serverCount']
462
+ }
463
+ end
464
+ columns = [
465
+ {:active => {:display_name => ""}},
466
+ {:id => {:width => 10}},
467
+ {:name => {:width => 16}},
468
+ {:location => {:width => 32}},
469
+ {:cloud_count => {:display_name => "Clouds"}},
470
+ {:server_count => {:display_name => "Hosts"}}
471
+ ]
472
+ print table_color
473
+ tp rows, columns
474
+ print reset
475
+ end
476
+
477
+ def add_group_option_types()
478
+ tmp_option_types = [
479
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
480
+ {'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
481
+ {'fieldName' => 'location', 'fieldLabel' => 'Location', 'type' => 'text', 'required' => false, 'displayOrder' => 3}
482
+ ]
483
+
484
+ # Advanced Options
485
+ # TODO: Service Registry
486
+
487
+ return tmp_option_types
488
+ end
489
+
490
+ def update_group_option_types()
491
+ add_group_option_types().collect {|it| it['required'] = false; it }
492
+ end
493
+
494
+ # todo: This belongs elsewhere, like module Morpheus::Cli::ActiveGroups
495
+
496
+ public
10
497
 
11
- def initialize()
12
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
13
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
14
- @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
15
- @active_groups = ::Morpheus::Cli::Groups.load_group_file
16
-
17
- end
18
-
19
- def handle(args)
20
- if @access_token.empty?
21
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
22
- return 1
23
- end
24
- if args.empty?
25
- puts "\nUsage: morpheus groups [list,add,remove] [name]\n\n"
26
- return
27
- end
28
-
29
- case args[0]
30
- when 'list'
31
- list(args[1..-1])
32
- when 'add'
33
- add(args[1..-1])
34
- when 'use'
35
- use(args[1..-1])
36
- when 'remove'
37
- remove(args[1..-1])
38
- else
39
- puts "\nUsage: morpheus groups [list,add,remove] [name]\n\n"
40
- end
41
- end
42
-
43
- def add(args)
44
- options = {}
45
- params = {}
46
- optparse = OptionParser.new do|opts|
47
- opts.banner = "Usage: morpheus groups add [name] [--location]"
48
- opts.on( '-l', '--location LOCATION', "Location" ) do |desc|
49
- params[:location] = desc
50
- end
51
- build_common_options(opts, options, [])
52
- end
53
- optparse.parse(args)
54
- if args.count < 1
55
- puts "\n#{optparse.banner}\n\n"
56
- exit 1
57
- end
58
- group = {name: args[0], location: params[:location]}
59
- begin
60
- @groups_interface.create(group)
61
- list([])
62
- rescue RestClient::Exception => e
63
- print_rest_exception(e, options)
64
- exit 1
65
- end
66
- end
67
-
68
- def remove(args)
69
- options = {}
70
- optparse = OptionParser.new do|opts|
71
- opts.banner = "Usage: morpheus groups remove [name]"
72
- build_common_options(opts, options, [:auto_confirm])
73
- end
74
- optparse.parse(args)
75
- if args.count < 1
76
- puts "\n#{optparse.banner}\n\n"
77
- exit 1
78
- end
79
-
80
- begin
81
- group_results = @groups_interface.get(args[0])
82
- if group_results['groups'].empty?
83
- print_red_alert "Group not found by name #{args[0]}"
84
- exit 1
85
- end
86
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the group #{group_results['groups'][0]['name']}?")
87
- exit
88
- end
89
- @groups_interface.destroy(group_results['groups'][0]['id'])
90
- list([])
91
- rescue RestClient::Exception => e
92
- print_rest_exception(e, options)
93
- exit 1
94
- end
95
- end
96
-
97
- def list(args)
98
- options = {}
99
- optparse = OptionParser.new do|opts|
100
- opts.banner = "Usage: morpheus groups list"
101
- build_common_options(opts, options, [:json])
102
- end
103
- optparse.parse(args)
104
- begin
105
- json_response = @groups_interface.get()
106
- if options[:json]
107
- print JSON.pretty_generate(json_response)
108
- return
109
- end
110
- groups = json_response['groups']
111
- print "\n" ,cyan, bold, "Morpheus Groups\n","==================", reset, "\n\n"
112
- if groups.empty?
113
- puts yellow,"No groups currently configured.",reset
114
- else
115
- groups.each do |group|
116
- if @active_groups[@appliance_name.to_sym] == group['id']
117
- print cyan, bold, "=> #{group['name']} - #{group['location']}",reset,"\n"
118
- else
119
- print cyan, "= #{group['name']} - #{group['location']}\n",reset
120
- end
121
- end
122
- end
123
- print reset,"\n\n"
124
-
125
- rescue RestClient::Exception => e
126
- print_rest_exception(e, options)
127
- exit 1
128
- end
129
- end
130
-
131
- def use(args)
132
- options = {}
133
- optparse = OptionParser.new do|opts|
134
- opts.banner = "Usage: morpheus groups use [name]"
135
- build_common_options(opts, options, [])
136
- end
137
- optparse.parse(args)
138
- if args.length < 1
139
- puts "\n#{optparse.banner}\n\n"
140
- exit 1
141
- end
142
- begin
143
- json_response = @groups_interface.get(args[0])
144
- groups = json_response['groups']
145
- if groups.length > 0
146
- @active_groups[@appliance_name.to_sym] = groups[0]['id']
147
- ::Morpheus::Cli::Groups.save_groups(@active_groups)
148
- list([])
149
- else
150
- print_red_alert "Group not found by name #{args[0]}"
151
- end
152
- rescue RestClient::Exception => e
153
- print_rest_exception(e, options)
154
- exit 1
155
- end
156
- end
157
-
158
- # Provides the current active group information
159
- def self.active_group
160
- appliance_name, appliance_url = Morpheus::Cli::Remote.active_appliance
161
- if !defined?(@@groups)
162
- @@groups = load_group_file
163
- end
164
- return @@groups[appliance_name.to_sym]
165
- end
166
-
167
-
168
-
169
- def self.load_group_file
170
- remote_file = groups_file_path
171
- if File.exist? remote_file
172
- return YAML.load_file(remote_file)
173
- else
174
- {}
175
- end
176
- end
177
-
178
- def self.groups_file_path
179
- home_dir = Dir.home
180
- morpheus_dir = File.join(home_dir,".morpheus")
181
- if !Dir.exist?(morpheus_dir)
182
- Dir.mkdir(morpheus_dir)
183
- end
184
- return File.join(morpheus_dir,"groups")
185
- end
186
-
187
- def self.save_groups(group_map)
188
- File.open(groups_file_path, 'w') {|f| f.write group_map.to_yaml } #Store
189
- end
498
+ @@groups = nil
499
+
500
+ class << self
501
+ include Term::ANSIColor
502
+ # Provides the current active group information
503
+ def active_groups_map
504
+ @@groups ||= load_group_file || {}
505
+ end
506
+
507
+ def active_groups
508
+ active_groups_map
509
+ end
510
+
511
+ # Provides the current active group information (just the ID right now)
512
+ # appliance_name should probably be required.. or just use this instead: Groups.active_groups[appliance_name]
513
+ def active_group(appliance_name=nil)
514
+ if appliance_name == nil
515
+ appliance_name, appliance_url = Morpheus::Cli::Remote.active_appliance
516
+ end
517
+ if !appliance_name
518
+ return nil
519
+ end
520
+ return active_groups_map[appliance_name.to_sym]
521
+ end
522
+
523
+ # alias (unused)
524
+ def active_group_id(appliance_name=nil)
525
+ active_group(appliance_name)
526
+ end
527
+
528
+ def set_active_group(appliance_name, group_id)
529
+ the_groups = active_groups_map
530
+ the_groups[appliance_name.to_sym] = group_id
531
+ save_groups(the_groups)
532
+ end
533
+
534
+ def clear_active_group(appliance_name)
535
+ the_groups = active_groups_map
536
+ the_groups.delete(appliance_name.to_sym)
537
+ save_groups(the_groups)
538
+ end
539
+
540
+ def load_group_file
541
+ fn = groups_file_path
542
+ if File.exist? fn
543
+ print "#{dark} #=> loading groups file #{fn}#{reset}\n" if Morpheus::Logging.debug?
544
+ return YAML.load_file(fn)
545
+ else
546
+ {}
547
+ end
548
+ end
549
+
550
+ def groups_file_path
551
+ return File.join(Morpheus::Cli.home_directory, "groups")
552
+ end
553
+
554
+ def save_groups(groups_map)
555
+ File.open(groups_file_path, 'w') {|f| f.write groups_map.to_yaml } #Store
556
+ FileUtils.chmod(0600, groups_file_path)
557
+ @@groups = groups_map
558
+ end
559
+
560
+ end
561
+
190
562
  end