morpheus-cli 2.10.0 → 2.10.1

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.
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