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
@@ -1,116 +1,124 @@
1
1
  require 'io/console'
2
2
  require 'optparse'
3
- require 'filesize'
4
3
  require 'morpheus/cli/cli_command'
5
4
 
6
5
  class Morpheus::Cli::InstanceTypes
7
6
  include Morpheus::Cli::CliCommand
8
7
 
9
- def initialize()
10
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
11
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
12
- @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
13
- end
8
+ register_subcommands :list, :get
9
+ alias_subcommand :details, :get
10
+ set_default_subcommand :list
14
11
 
12
+ def initialize()
13
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
14
+ end
15
15
 
16
- def handle(args)
17
- if @access_token.empty?
18
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
19
- return 1
20
- end
21
- if args.empty?
22
- puts "\nUsage: morpheus instance-types [list,details] [name]\n\n"
23
- return
24
- end
16
+ def connect(opts)
17
+ @api_client = establish_remote_appliance_connection(opts)
18
+ @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
19
+ end
25
20
 
26
- case args[0]
27
- when 'list'
28
- list(args[1..-1])
29
- when 'details'
30
- details(args[1..-1])
31
- else
32
- puts "\nUsage: morpheus instance-types [list,details] [name]\n\n"
33
- end
34
- end
21
+ def handle(args)
22
+ handle_subcommand(args)
23
+ end
35
24
 
25
+ def get(args)
26
+ options = {}
27
+ optparse = OptionParser.new do|opts|
28
+ opts.banner = subcommand_usage("[name]")
29
+ build_common_options(opts, options, [:json, :dry_run])
30
+ end
31
+ optparse.parse!(args)
32
+ if args.count < 1
33
+ puts optparse
34
+ exit
35
+ end
36
+ name = args[0]
37
+ connect(options)
38
+ begin
39
+ if options[:dry_run]
40
+ print_dry_run @instance_types_interface.dry.get({name: name})
41
+ return
42
+ end
43
+ json_response = @instance_types_interface.get({name: name})
36
44
 
37
- def details(args)
38
- options = {}
39
- optparse = OptionParser.new do|opts|
40
- opts.banner = "Usage: morpheus instance-type details [name]"
41
- build_common_options(opts, options, [:json])
42
- end
43
- optparse.parse(args)
44
- if args.count < 1
45
- puts "\n#{optparse.banner}\n\n"
46
- return
47
- end
48
- name = args[0]
49
- begin
50
- json_response = @instance_types_interface.get({name: name})
45
+ if options[:json]
46
+ print JSON.pretty_generate(json_response), "\n"
47
+ return
48
+ end
51
49
 
52
- if options[:json]
53
- print JSON.pretty_generate(json_response), "\n"
54
- return
55
- end
50
+ instance_type = json_response['instanceTypes'][0]
56
51
 
57
- instance_type = json_response['instanceTypes'][0]
52
+ if instance_type.nil?
53
+ puts yellow,"No instance type found by name #{name}.",reset
54
+ else
55
+ print "\n" ,cyan, bold, "Instance Type Details\n","==================", reset, "\n\n"
56
+ versions = instance_type['versions'].join(', ')
57
+ print cyan, "= #{instance_type['name']} (#{instance_type['code']}) - #{versions}\n"
58
+ layout_names = instance_type['instanceTypeLayouts'].collect { |layout| layout['name'] }.uniq.sort
59
+ layout_names.each do |layout_name|
60
+ print green, " - #{layout_name}\n",reset
61
+ end
62
+ # instance_type['instanceTypeLayouts'].each do |layout|
63
+ # print green, " - #{layout['name']}\n",reset
64
+ # end
65
+ print reset,"\n"
66
+ end
58
67
 
59
- if instance_type.nil?
60
- puts yellow,"No instance type found by name #{name}.",reset
61
- else
62
- print "\n" ,cyan, bold, "Instance Type Details\n","==================", reset, "\n\n"
63
- versions = instance_type['versions'].join(', ')
64
- print cyan, "= #{instance_type['name']} (#{instance_type['code']}) - #{versions}\n"
65
- instance_type['instanceTypeLayouts'].each do |layout|
66
- print green, " - #{layout['name']}\n",reset
67
- end
68
- print reset,"\n\n"
69
- end
68
+ rescue RestClient::Exception => e
69
+ print_rest_exception(e, options)
70
+ exit 1
71
+ end
72
+ end
70
73
 
71
- rescue RestClient::Exception => e
72
- print_rest_exception(e, options)
73
- exit 1
74
- end
75
- end
74
+ def list(args)
75
+ options = {}
76
+ optparse = OptionParser.new do|opts|
77
+ opts.banner = subcommand_usage()
78
+ build_common_options(opts, options, [:list, :json, :dry_run])
79
+ end
80
+ optparse.parse!(args)
81
+ connect(options)
82
+ begin
83
+ params = {}
84
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
85
+ params[k] = options[k] unless options[k].nil?
86
+ end
76
87
 
77
- def list(args)
78
- options = {}
79
- optparse = OptionParser.new do|opts|
80
- build_common_options(opts, options, [:list, :json])
81
- end
82
- optparse.parse(args)
83
- begin
84
- params = {}
85
- [:phrase, :offset, :max, :sort, :direction].each do |k|
86
- params[k] = options[k] unless options[k].nil?
87
- end
88
+ if options[:dry_run]
89
+ print_dry_run @instance_types_interface.dry.get(params)
90
+ return
91
+ end
92
+ json_response = @instance_types_interface.get(params)
88
93
 
89
- json_response = @instance_types_interface.get(params)
94
+ if options[:json]
95
+ print JSON.pretty_generate(json_response), "\n"
96
+ return
97
+ end
90
98
 
91
- if options[:json]
92
- print JSON.pretty_generate(json_response), "\n"
93
- return
94
- end
99
+ instance_types = json_response['instanceTypes']
100
+ print "\n" ,cyan, bold, "Morpheus Instance Types\n","==================", reset, "\n\n"
101
+ if instance_types.empty?
102
+ puts yellow,"No instance types currently configured.",reset
103
+ else
104
+ instance_types.each do |instance_type|
105
+ versions = instance_type['versions'].join(', ')
106
+ print cyan, "= #{instance_type['name']} (#{instance_type['code']}) - #{versions}\n"
107
+ layout_names = instance_type['instanceTypeLayouts'].collect { |layout| layout['name'] }.uniq.sort
108
+ layout_names.each do |layout_name|
109
+ print green, " - #{layout_name}\n",reset
110
+ end
111
+ # instance_type['instanceTypeLayouts'].each do |layout|
112
+ # print green, " - #{layout['name']}\n",reset
113
+ # end
114
+ #print JSON.pretty_generate(instance_type['instanceTypeLayouts'].first), "\n"
115
+ end
95
116
 
96
- instance_types = json_response['instanceTypes']
97
- print "\n" ,cyan, bold, "Morpheus Instance Types\n","==================", reset, "\n\n"
98
- if instance_types.empty?
99
- puts yellow,"No instance types currently configured.",reset
100
- else
101
- instance_types.each do |instance_type|
102
- versions = instance_type['versions'].join(', ')
103
- print cyan, "= #{instance_type['name']} (#{instance_type['code']}) - #{versions}\n"
104
- instance_type['instanceTypeLayouts'].each do |layout|
105
- print green, " - #{layout['name']}\n",reset
106
- end
107
- end
108
- end
109
- print reset,"\n\n"
110
-
111
- rescue RestClient::Exception => e
112
- print_rest_exception(e, options)
113
- exit 1
114
- end
115
- end
117
+ end
118
+ print reset,"\n"
119
+ rescue RestClient::Exception => e
120
+ print_rest_exception(e, options)
121
+ exit 1
122
+ end
123
+ end
116
124
  end
@@ -5,198 +5,127 @@ require 'filesize'
5
5
  require 'table_print'
6
6
  require 'morpheus/cli/cli_command'
7
7
  require 'morpheus/cli/mixins/provisioning_helper'
8
+ require 'morpheus/cli/option_types'
8
9
 
9
10
  class Morpheus::Cli::Instances
10
11
  include Morpheus::Cli::CliCommand
11
12
  include Morpheus::Cli::ProvisioningHelper
12
13
 
13
- def initialize()
14
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
15
-
16
- end
17
-
18
- def connect(opts)
19
- if opts[:remote]
20
- @appliance_url = opts[:remote]
21
- @appliance_name = opts[:remote]
22
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
23
- else
24
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
25
- end
26
- @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
27
- @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
28
- @task_sets_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).task_sets
29
- @logs_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).logs
30
- @tasks_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).tasks
31
- @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
32
- @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
33
- @provision_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).provision_types
34
- @options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
35
- @active_groups = ::Morpheus::Cli::Groups.load_group_file
36
- if @access_token.empty?
37
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
38
- exit 1
39
- end
40
- end
41
-
42
-
43
- def handle(args)
44
- usage = "Usage: morpheus instances [list,details,add,update,remove,stop,start,restart,backup,run-workflow,stop-service,start-service,restart-service,resize,upgrade,clone,envs,setenv,delenv] [name]"
45
- if args.empty?
46
- puts "\n#{usage}\n\n"
47
- return
48
- end
49
-
50
- case args[0]
51
- when 'list'
52
- list(args[1..-1])
53
- when 'details'
54
- details(args[1..-1])
55
- when 'add'
56
- add(args[1..-1])
57
- when 'update'
58
- update(args[1..-1])
59
- when 'remove'
60
- remove(args[1..-1])
61
- when 'stop'
62
- stop(args[1..-1])
63
- when 'start'
64
- start(args[1..-1])
65
- when 'restart'
66
- restart(args[1..-1])
67
- when 'stop-service'
68
- stop_service(args[1..-1])
69
- when 'start-service'
70
- start_service(args[1..-1])
71
- when 'restart-service'
72
- restart_service(args[1..-1])
73
- when 'resize'
74
- resize(args[1..-1])
75
- when 'run-workflow'
76
- run_workflow(args[1..-1])
77
- when 'stats'
78
- stats(args[1..-1])
79
- when 'logs'
80
- logs(args[1..-1])
81
- when 'envs'
82
- envs(args[1..-1])
83
- when 'setenv'
84
- setenv(args[1..-1])
85
- when 'delenv'
86
- delenv(args[1..-1])
87
- when 'firewall-disable'
88
- firewall_disable(args[1..-1])
89
- when 'firewall-enable'
90
- firewall_enable(args[1..-1])
91
- when 'security-groups'
92
- security_groups(args[1..-1])
93
- when 'apply-security-groups'
94
- apply_security_groups(args[1..-1])
95
- when 'backup'
96
- backup(args[1..-1])
97
- else
98
- puts "\n#{usage}\n\n"
99
- exit 127
100
- end
101
- end
102
-
103
- def add(args)
104
- options = {}
105
- optparse = OptionParser.new do|opts|
106
- opts.banner = "Usage: morpheus instances add [type] [name]"
107
- opts.on( '-g', '--group GROUP', "Group Name" ) do |val|
108
- options[:group_name] = val
109
- end
110
- opts.on( '-G', '--group-id ID', "Group Id" ) do |val|
111
- options[:group_id] = val
112
- end
113
- opts.on( '-c', '--cloud CLOUD', "Cloud Name" ) do |val|
114
- options[:cloud_name] = val
115
- end
116
- # this conflicts with --nocolor option
117
- # opts.on( '-C', '--cloud CLOUD', "Cloud Id" ) do |val|
118
- # options[:cloud] = val
119
- # end
120
- opts.on( '-t', '--type CODE', "Instance Type" ) do |val|
121
- options[:instance_type_code] = val
122
- end
123
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
124
-
125
- end
126
-
127
- optparse.parse(args)
128
- connect(options)
129
-
130
- # support old format of `instance add TYPE NAME`
131
- if args[0] && args[0] !~ /\A\-/
132
- options[:instance_type_code] = args[0]
133
- if args[1] && args[1] !~ /\A\-/
134
- options[:instance_name] = args[1]
135
- end
136
- end
137
-
138
- # use active group by default
139
- if !options[:group_name] && !options[:group_id]
140
- options[:group_id] = @active_groups[@appliance_name.to_sym]
141
- end
142
-
143
- options[:name_required] = true
144
-
145
- begin
146
-
147
- payload = prompt_new_instance(options)
148
-
149
- if options[:dry_run]
150
- print_dry_run("POST #{@appliance_url}/api/instances", payload)
151
- return
152
- end
153
- json_response = @instances_interface.create(payload)
154
- if options[:json]
155
- print JSON.pretty_generate(json_response)
156
- print "\n"
157
- else
158
- instance_name = json_response["instance"]["name"]
159
- print_green_success "Provisioning instance #{instance_name}"
160
- list([])
161
- end
162
- rescue RestClient::Exception => e
163
- print_rest_exception(e, options)
164
- exit 1
165
- end
166
- end
167
-
168
- def update(args)
169
- usage = "Usage: morpheus instances update [name] [options]"
14
+ register_subcommands :list, :get, :add, :update, :remove, :stats, :stop, :start, :restart, :suspend, :eject, :backup, :backups, :stop_service, :start_service, :restart_service, :resize, :upgrade, :clone, :envs, :setenv, :delenv, :security_groups, :apply_security_groups, :firewall_enable, :firewall_disable, :run_workflow, :import_snapshot, :console, :status_check
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
+ @instances_interface = @api_client.instances
25
+ @task_sets_interface = @api_client.task_sets
26
+ @logs_interface = @api_client.logs
27
+ @tasks_interface = @api_client.tasks
28
+ @instance_types_interface = @api_client.instance_types
29
+ @clouds_interface = @api_client.clouds
30
+ @provision_types_interface = @api_client.provision_types
31
+ @options_interface = @api_client.options
32
+ @active_group_id = Morpheus::Cli::Groups.active_group
33
+ end
34
+
35
+ def handle(args)
36
+ handle_subcommand(args)
37
+ end
38
+
39
+ def add(args)
170
40
  options = {}
171
41
  optparse = OptionParser.new do|opts|
172
- opts.banner = usage
173
- build_common_options(opts, options, [:options, :json, :dry_run])
42
+ opts.banner = subcommand_usage("[type] [name]")
43
+ opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
44
+ options[:group] = val
45
+ end
46
+ opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
47
+ options[:cloud] = val
48
+ end
49
+ opts.on( '-t', '--type CODE', "Instance Type" ) do |val|
50
+ options[:instance_type_code] = val
51
+ end
52
+ opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
53
+ options[:copies] = val.to_i
54
+ end
55
+ opts.on("--layout-size NUMBER", Integer, "Apply a multiply factor of containers/vms within the instance") do |val|
56
+ options[:layout_size] = val.to_i
57
+ end
58
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
59
+ end
60
+
61
+ optparse.parse!(args)
62
+ connect(options)
63
+
64
+ # support old format of `instance add TYPE NAME`
65
+ if args[0]
66
+ options[:instance_type_code] = args[0]
67
+ end
68
+ if args[1]
69
+ options[:instance_name] = args[1]
70
+ end
71
+ # use active group by default
72
+ options[:group] ||= @active_group_id
73
+
74
+ options[:name_required] = true
75
+ begin
76
+
77
+ payload = prompt_new_instance(options)
78
+ payload[:copies] = options[:copies] if options[:copies] && options[:copies] > 0
79
+ payload[:layoutSize] = options[:layout_size] if options[:layout_size] && options[:layout_size] > 0
80
+ if options[:dry_run]
81
+ print_dry_run @instances_interface.dry.create(payload)
82
+ return
83
+ end
84
+ json_response = @instances_interface.create(payload)
85
+ if options[:json]
86
+ print JSON.pretty_generate(json_response)
87
+ print "\n"
88
+ else
89
+ instance_name = json_response["instance"]["name"]
90
+ print_green_success "Provisioning instance #{instance_name}"
91
+ list([])
92
+ end
93
+ rescue RestClient::Exception => e
94
+ print_rest_exception(e, options)
95
+ exit 1
96
+ end
97
+ end
98
+
99
+ def update(args)
100
+ usage = "Usage: morpheus instances update [name] [options]"
101
+ options = {}
102
+ optparse = OptionParser.new do|opts|
103
+ opts.banner = subcommand_usage("[name]")
104
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
174
105
  end
175
- optparse.parse(args)
106
+ optparse.parse!(args)
176
107
  if args.count < 1
177
- puts "\n#{optparse.banner}\n\n"
178
- exit 1
179
- end
108
+ puts optparse
109
+ exit 1
110
+ end
180
111
  connect(options)
181
112
 
182
113
  begin
183
-
184
- instance = find_instance_by_name_or_id(args[0])
185
114
 
186
- # group = find_group_from_options(options)
115
+ instance = find_instance_by_name_or_id(args[0])
187
116
 
188
- payload = {
189
- 'instance' => {id: instance["id"]}
190
- }
117
+ payload = {
118
+ 'instance' => {id: instance["id"]}
119
+ }
191
120
 
192
- update_instance_option_types = [
193
- {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this instance'},
194
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false},
195
- {'fieldName' => 'instanceContext', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => instance_context_options()},
196
- {'fieldName' => 'tags', 'fieldLabel' => 'Tags', 'type' => 'text', 'required' => false}
197
- ]
121
+ update_instance_option_types = [
122
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this instance'},
123
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false},
124
+ {'fieldName' => 'instanceContext', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => instance_context_options()},
125
+ {'fieldName' => 'tags', 'fieldLabel' => 'Tags', 'type' => 'text', 'required' => false}
126
+ ]
198
127
 
199
- params = options[:options] || {}
128
+ params = options[:options] || {}
200
129
 
201
130
  if params.empty?
202
131
  puts "\n#{usage}\n"
@@ -209,13 +138,11 @@ class Morpheus::Cli::Instances
209
138
  params = params.select {|k,v| instance_keys.include?(k) }
210
139
  params['tags'] = params['tags'].split(',').collect {|it| it.to_s.strip }.compact.uniq if params['tags']
211
140
  payload['instance'].merge!(params)
212
-
213
- json_response = @instances_interface.update(instance["id"], payload)
214
-
215
141
  if options[:dry_run]
216
- print_dry_run("PUT #{@appliance_url}/api/instances", payload)
217
- return
218
- end
142
+ print_dry_run @instances_interface.dry.update(instance["id"], payload)
143
+ return
144
+ end
145
+ json_response = @instances_interface.update(instance["id"], payload)
219
146
 
220
147
  if options[:json]
221
148
  print JSON.pretty_generate(json_response)
@@ -223,838 +150,1237 @@ class Morpheus::Cli::Instances
223
150
  else
224
151
  print_green_success "Updated instance #{instance['name']}"
225
152
  list([])
226
- # details_options = [payload['app']['name']]
227
- # details(details_options)
228
153
  end
229
154
 
230
155
  rescue RestClient::Exception => e
231
156
  print_rest_exception(e, options)
232
157
  exit 1
233
158
  end
234
- end
235
-
236
- def stats(args)
237
- options = {}
238
- optparse = OptionParser.new do|opts|
239
- opts.banner = "Usage: morpheus instances stats [name]"
240
- build_common_options(opts, options, [:json, :remote])
241
- end
242
- if args.count < 1
243
- puts "\n#{optparse.banner}\n\n"
244
- return
245
- end
246
- optparse.parse(args)
247
- connect(options)
248
- begin
249
- instance = find_instance_by_name_or_id(args[0])
250
- json_response = @instances_interface.get(instance['id'])
251
- if options[:json]
252
- print JSON.pretty_generate(json_response)
253
- return
254
- end
255
- instance = json_response['instance']
256
- stats = json_response['stats'] || {}
257
- if options[:json]
258
- print JSON.pretty_generate(stats)
259
- print "\n"
260
- else
261
- print "\n" ,cyan, bold, "#{instance['name']} (#{instance['instanceType']['name']})\n","==================", "\n\n", reset, cyan
262
- stats_map = {}
263
- stats_map[:memory] = "#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}"
264
- stats_map[:storage] = "#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}"
265
- stats_map[:cpu] = "#{stats['usedCpu'].to_f.round(2)}%"
266
- tp [stats_map], :memory,:storage,:cpu
267
- print reset, "\n"
268
- end
269
- rescue RestClient::Exception => e
270
- print_rest_exception(e, options)
271
- exit 1
272
- end
273
- end
274
-
275
- def logs(args)
276
- options = {}
277
- optparse = OptionParser.new do|opts|
278
- opts.banner = "Usage: morpheus instances logs [name]"
279
- opts.on( '-n', '--node NODE_ID', "Scope logs to specific Container or VM" ) do |node_id|
280
- options[:node_id] = node_id.to_i
281
- end
282
- build_common_options(opts, options, [:list, :json, :remote])
283
- end
284
- if args.count < 1
285
- puts "\n#{optparse.banner}\n\n"
286
- return
287
- end
288
- optparse.parse(args)
289
- connect(options)
290
- begin
291
- instance = find_instance_by_name_or_id(args[0])
292
- container_ids = instance['containers']
293
- if options[:node_id] && container_ids.include?(options[:node_id])
294
- container_ids = [options[:node_id]]
295
- end
296
- logs = @logs_interface.container_logs(container_ids, { max: options[:max] || 100, offset: options[:offset] || 0, query: options[:phrase]})
297
- if options[:json]
298
- puts logs
299
- else
300
- logs['data'].reverse.each do |log_entry|
301
- log_level = ''
302
- case log_entry['level']
303
- when 'INFO'
304
- log_level = "#{blue}#{bold}INFO#{reset}"
305
- when 'DEBUG'
306
- log_level = "#{white}#{bold}DEBUG#{reset}"
307
- when 'WARN'
308
- log_level = "#{yellow}#{bold}WARN#{reset}"
309
- when 'ERROR'
310
- log_level = "#{red}#{bold}ERROR#{reset}"
311
- when 'FATAL'
312
- log_level = "#{red}#{bold}FATAL#{reset}"
313
- end
314
- puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}"
315
- end
316
- print reset,"\n"
317
- end
318
- rescue RestClient::Exception => e
319
- print_rest_exception(e, options)
320
- exit 1
321
- end
322
- end
323
-
324
- def details(args)
325
- options = {}
326
- optparse = OptionParser.new do|opts|
327
- opts.banner = "Usage: morpheus instances details [name]"
328
- build_common_options(opts, options, [:json, :remote])
329
- end
330
- if args.count < 1
331
- puts "\n#{optparse.banner}\n\n"
332
- exit 1
333
- end
334
- optparse.parse(args)
335
- connect(options)
336
- begin
337
- instance = find_instance_by_name_or_id(args[0])
338
- json_response = @instances_interface.get(instance['id'])
339
- if options[:json]
340
- print JSON.pretty_generate(json_response)
341
- return
342
- end
343
- instance = json_response['instance']
344
- stats = json_response['stats'] || {}
345
- # load_balancers = stats = json_response['loadBalancers'] || {}
346
-
347
- status_string = instance['status']
348
- if status_string == 'running'
349
- status_string = "#{green}#{status_string.upcase}#{cyan}"
350
- elsif status_string == 'stopped' or status_string == 'failed'
351
- status_string = "#{red}#{status_string.upcase}#{cyan}"
352
- elsif status_string == 'unknown'
353
- status_string = "#{white}#{status_string.upcase}#{cyan}"
354
- else
355
- status_string = "#{yellow}#{status_string.upcase}#{cyan}"
356
- end
357
- connection_string = ''
358
- if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
359
- connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
360
- end
361
-
362
- print "\n" ,cyan, bold, "Instance Details\n","==================", reset, "\n\n"
363
- print cyan
364
- puts "ID: #{instance['id']}"
365
- puts "Name: #{instance['name']}"
366
- puts "Description: #{instance['description']}"
367
- puts "Group: #{instance['group'] ? instance['group']['name'] : ''}"
368
- puts "Cloud: #{instance['cloud'] ? instance['cloud']['name'] : ''}"
369
- puts "Type: #{instance['instanceType']['name']}"
370
- puts "Environment: #{instance['instanceContext']}"
371
- puts "Nodes: #{instance['containers'] ? instance['containers'].count : 0}"
372
- puts "Connection: #{connection_string}"
373
- #puts "Account: #{instance['account'] ? instance['account']['name'] : ''}"
374
- puts "Status: #{status_string}"
375
- print cyan, "Memory: \t#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}\n"
376
- print cyan, "Storage: \t#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}\n\n",reset
377
-
378
- #puts instance
379
- rescue RestClient::Exception => e
380
- print_rest_exception(e, options)
381
- exit 1
382
- end
383
- end
384
-
385
- def envs(args)
386
- options = {}
387
- optparse = OptionParser.new do|opts|
388
- opts.banner = "Usage: morpheus instances envs [name]"
389
- build_common_options(opts, options, [:json, :remote])
390
- end
391
- if args.count < 1
392
- puts "\n#{optparse.banner}\n\n"
393
- exit 1
394
- end
395
- optparse.parse(args)
396
- connect(options)
397
- begin
398
- instance = find_instance_by_name_or_id(args[0])
399
- env_results = @instances_interface.get_envs(instance['id'])
400
- print "\n" ,cyan, bold, "#{instance['name']} (#{instance['instanceType']['name']})\n","==================", "\n\n", reset, cyan
401
- envs = env_results['envs'] || {}
402
- if env_results['readOnlyEnvs']
403
- envs += env_results['readOnlyEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") || v['masked'] ? "********" : v['value'], :export => true}}
404
- end
405
- tp envs, :name, :value, :export
406
- print "\n" ,cyan, bold, "Imported Envs\n","==================", "\n\n", reset, cyan
407
- imported_envs = env_results['importedEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") || v['masked'] ? "********" : v['value']}}
408
- tp imported_envs
409
- print reset, "\n"
410
-
411
- rescue RestClient::Exception => e
412
- print_rest_exception(e, options)
413
- exit 1
414
- end
415
- end
416
-
417
- def setenv(args)
418
- options = {}
419
-
420
- optparse = OptionParser.new do|opts|
421
- opts.banner = "Usage: morpheus instances setenv INSTANCE NAME VALUE [-e]"
422
- opts.on( '-e', "Exportable" ) do |exportable|
423
- options[:export] = exportable
424
- end
425
- opts.on( '-M', "Masked" ) do |masked|
426
- options[:masked] = masked
427
- end
428
- build_common_options(opts, options, [:json, :remote])
429
- end
430
- if args.count < 3
431
- puts "\n#{optparse.banner}\n\n"
432
- exit 1
433
- end
434
- optparse.parse(args)
435
- connect(options)
436
- begin
437
- instance = find_instance_by_name_or_id(args[0])
438
- evar = {name: args[1], value: args[2], export: options[:export], masked: options[:masked]}
439
- params = {}
440
- @instances_interface.create_env(instance['id'], [evar])
441
- envs([args[0]])
442
- rescue RestClient::Exception => e
443
- print_rest_exception(e, options)
444
- exit 1
445
- end
446
- end
447
-
448
- def delenv(args)
449
- options = {}
450
- optparse = OptionParser.new do|opts|
451
- opts.banner = "Usage: morpheus instances setenv INSTANCE NAME"
452
- build_common_options(opts, options, [:json, :remote])
453
- end
454
- if args.count < 2
455
- puts "\n#{optparse.banner}\n\n"
456
- exit 1
457
- end
458
- optparse.parse(args)
459
- connect(options)
460
- begin
461
- instance = find_instance_by_name_or_id(args[0])
462
- @instances_interface.del_env(instance['id'], args[1])
463
- envs([args[0]])
464
- rescue RestClient::Exception => e
465
- print_rest_exception(e, options)
466
- exit 1
467
- end
468
- end
469
-
470
- def stop(args)
471
- options = {}
472
- optparse = OptionParser.new do|opts|
473
- opts.banner = "Usage: morpheus instances stop [name]"
474
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
475
- end
476
- if args.count < 1
477
- puts "\n#{optparse.banner}\n\n"
478
- exit 1
479
- end
480
- optparse.parse(args)
481
- connect(options)
482
- begin
483
- instance = find_instance_by_name_or_id(args[0])
484
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to stop this instance?", options)
485
- exit 1
486
- end
487
- json_response = @instances_interface.stop(instance['id'])
488
- if options[:json]
489
- print JSON.pretty_generate(json_response)
490
- print "\n"
491
- end
492
- return
493
- rescue RestClient::Exception => e
494
- print_rest_exception(e, options)
495
- exit 1
496
- end
497
- end
498
-
499
- def start(args)
500
- options = {}
501
- optparse = OptionParser.new do|opts|
502
- opts.banner = "Usage: morpheus instances start [name]"
503
- build_common_options(opts, options, [:json, :remote])
504
- end
505
- if args.count < 1
506
- puts "\n#{optparse.banner}\n\n"
507
- exit 1
508
- end
509
- optparse.parse(args)
510
- connect(options)
511
- begin
512
- instance = find_instance_by_name_or_id(args[0])
513
- json_response = @instances_interface.start(instance['id'])
514
- if options[:json]
515
- print JSON.pretty_generate(json_response)
516
- print "\n"
517
- end
518
- return
519
- rescue RestClient::Exception => e
520
- print_rest_exception(e, options)
521
- exit 1
522
- end
523
- end
524
-
525
- def restart(args)
526
- options = {}
527
- optparse = OptionParser.new do|opts|
528
- opts.banner = "Usage: morpheus instances restart [name]"
529
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
530
- end
531
- if args.count < 1
532
- puts "\n#{optparse.banner}\n\n"
533
- exit 1
534
- end
535
- optparse.parse(args)
536
- connect(options)
537
- begin
538
- instance = find_instance_by_name_or_id(args[0])
539
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to restart this instance?", options)
540
- exit 1
541
- end
542
- json_response = @instances_interface.restart(instance['id'])
543
- if options[:json]
544
- print JSON.pretty_generate(json_response)
545
- print "\n"
546
- end
547
- return
548
- rescue RestClient::Exception => e
549
- print_rest_exception(e, options)
550
- exit 1
551
- end
552
- end
553
-
554
- def stop_service(args)
555
- options = {}
556
- optparse = OptionParser.new do|opts|
557
- opts.banner = "Usage: morpheus instances stop-service [name]"
558
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
559
- end
560
- if args.count < 1
561
- puts "\n#{optparse.banner}\n\n"
562
- exit 1
563
- end
564
- optparse.parse(args)
565
- connect(options)
566
- begin
567
- instance = find_instance_by_name_or_id(args[0])
568
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to stop this instance?", options)
569
- exit 1
570
- end
571
- json_response = @instances_interface.stop(instance['id'],false)
572
- if options[:json]
573
- print JSON.pretty_generate(json_response)
574
- print "\n"
575
- else
576
- puts "Stopping service on #{args[0]}"
577
- end
578
- return
579
- rescue RestClient::Exception => e
580
- print_rest_exception(e, options)
581
- exit 1
582
- end
583
- end
584
-
585
- def start_service(args)
586
- options = {}
587
- optparse = OptionParser.new do|opts|
588
- opts.banner = "Usage: morpheus instances start-service [name]"
589
- build_common_options(opts, options, [:json, :remote])
590
- end
591
- if args.count < 1
592
- puts "\n#{optparse.banner}\n\n"
593
- exit 1
594
- end
595
- optparse.parse(args)
596
- connect(options)
597
- begin
598
- instance = find_instance_by_name_or_id(args[0])
599
- json_response = @instances_interface.start(instance['id'],false)
600
- if options[:json]
601
- print JSON.pretty_generate(json_response)
602
- print "\n"
603
- else
604
- puts "Starting service on #{args[0]}"
605
- end
606
- return
607
- rescue RestClient::Exception => e
608
- print_rest_exception(e, options)
609
- exit 1
610
- end
611
- end
612
-
613
- def restart_service(args)
614
- options = {}
615
- optparse = OptionParser.new do|opts|
616
- opts.banner = "Usage: morpheus instances restart-service [name]"
617
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
618
- end
619
- if args.count < 1
620
- puts "\n#{optparse.banner}\n\n"
621
- exit 1
622
- end
623
- optparse.parse(args)
624
- connect(options)
625
- begin
626
- instance = find_instance_by_name_or_id(args[0])
627
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to restart this instance?", options)
628
- exit 1
629
- end
630
- json_response = @instances_interface.restart(instance['id'],false)
631
- if options[:json]
632
- print JSON.pretty_generate(json_response)
633
- print "\n"
634
- else
635
- puts "Restarting service on instance #{args[0]}"
636
- end
637
- return
638
- rescue RestClient::Exception => e
639
- print_rest_exception(e, options)
640
- exit 1
641
- end
642
- end
643
-
644
- def resize(args)
645
- options = {}
646
- optparse = OptionParser.new do|opts|
647
- opts.banner = "Usage: morpheus instances resize [name]"
648
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
649
- end
650
- if args.count < 1
651
- puts "\n#{optparse.banner}\n\n"
652
- exit 1
653
- end
654
- optparse.parse(args)
655
- connect(options)
656
- begin
657
- instance = find_instance_by_name_or_id(args[0])
658
-
659
- group_id = instance['group']['id']
660
- cloud_id = instance['cloud']['id']
661
- layout_id = instance['layout']['id']
662
-
663
- plan_id = instance['plan']['id']
664
- payload = {
665
- :instance => {:id => instance["id"]}
666
- }
667
-
668
- # avoid 500 error
669
- # payload[:servicePlanOptions] = {}
670
-
671
- puts "\nDue to limitations by most Guest Operating Systems, Disk sizes can only be expanded and not reduced.\nIf a smaller plan is selected, memory and CPU (if relevant) will be reduced but storage will not.\n\n"
672
-
673
- # prompt for service plan
674
- service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, layoutId: layout_id})
675
- service_plans = service_plans_json["plans"]
676
- service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"]} } # already sorted
677
- service_plans_dropdown.each do |plan|
678
- if plan['value'] && plan['value'].to_i == plan_id.to_i
679
- plan['name'] = "#{plan['name']} (current)"
680
- end
681
- end
682
- plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance'}],options[:options])
683
- service_plan = service_plans.find {|sp| sp["id"] == plan_prompt['servicePlan'].to_i }
684
- new_plan_id = service_plan["id"]
685
- #payload[:servicePlan] = new_plan_id # ew, this api uses servicePlanId instead
686
- payload[:servicePlanId] = new_plan_id
687
- payload[:instance][:plan] = {id: service_plan["id"]}
688
-
689
- volumes_response = @instances_interface.volumes(instance['id'])
690
- current_volumes = volumes_response['volumes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
691
-
692
- # prompt for volumes
693
- volumes = prompt_resize_volumes(current_volumes, service_plan, options, @api_client, {})
694
- if !volumes.empty?
695
- payload[:volumes] = volumes
696
- end
697
-
698
- # only amazon supports this option
699
- # for now, always do this
700
- payload[:deleteOriginalVolumes] = true
701
-
702
- if options[:dry_run]
703
- print_dry_run("PUT #{@appliance_url}/api/instances/#{instance['id']}/resize", payload)
704
- return
705
- end
706
- json_response = @instances_interface.resize(instance['id'], payload)
707
- if options[:json]
708
- print JSON.pretty_generate(json_response)
709
- print "\n"
710
- else
711
- print_green_success "Resizing instance #{instance['name']}"
712
- list([])
713
- end
714
-
715
- rescue RestClient::Exception => e
716
- print_rest_exception(e, options)
717
- exit 1
718
- end
719
- end
720
-
721
- def backup(args)
722
- options = {}
723
- optparse = OptionParser.new do|opts|
724
- opts.banner = "Usage: morpheus instances backup [name]"
725
- build_common_options(opts, options, [:json, :remote])
726
- end
727
- if args.count < 1
728
- puts "\n#{optparse.banner}\n\n"
729
- exit 1
730
- end
731
- optparse.parse(args)
732
- connect(options)
733
- begin
734
- instance = find_instance_by_name_or_id(args[0])
735
- json_response = @instances_interface.backup(instance['id'])
736
- if options[:json]
737
- print JSON.pretty_generate(json_response)
738
- print "\n"
739
- else
740
- puts "Backup initiated."
741
- end
742
- return
743
- rescue RestClient::Exception => e
744
- print_rest_exception(e, options)
745
- exit 1
746
- end
747
- end
748
-
749
- def list(args)
750
- options = {}
751
- optparse = OptionParser.new do|opts|
752
- opts.on( '-g', '--group GROUP', "Group Name" ) do |val|
753
- options[:group_name] = val
754
- end
755
- build_common_options(opts, options, [:list, :json, :remote])
756
- end
757
- optparse.parse(args)
758
- connect(options)
759
- begin
760
- params = {}
761
- group = find_group_from_options(options)
762
- if group
763
- params['site'] = group['id']
764
- end
765
- [:phrase, :offset, :max, :sort, :direction].each do |k|
766
- params[k] = options[k] unless options[k].nil?
767
- end
768
-
769
- json_response = @instances_interface.get(params)
770
- if options[:json]
771
- print JSON.pretty_generate(json_response)
772
- print "\n"
773
- else
774
- instances = json_response['instances']
775
-
776
- print "\n" ,cyan, bold, "Morpheus Instances\n","==================", reset, "\n\n"
777
- if instances.empty?
778
- puts yellow,"No instances currently configured.",reset
779
- else
780
- print cyan
781
- instance_table = instances.collect do |instance|
782
- status_string = instance['status']
783
- if status_string == 'running'
784
- status_string = "#{green}#{status_string.upcase}#{cyan}"
785
- elsif status_string == 'stopped' or status_string == 'failed'
786
- status_string = "#{red}#{status_string.upcase}#{cyan}"
787
- elsif status_string == 'unknown'
788
- status_string = "#{white}#{status_string.upcase}#{cyan}"
789
- else
790
- status_string = "#{yellow}#{status_string.upcase}#{cyan}"
791
- end
792
- connection_string = ''
793
- if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
794
- connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
795
- end
796
- {id: instance['id'], name: instance['name'], connection: connection_string, environment: instance['instanceContext'], nodes: instance['containers'].count, status: status_string, type: instance['instanceType']['name'], group: !instance['group'].nil? ? instance['group']['name'] : nil, cloud: !instance['cloud'].nil? ? instance['cloud']['name'] : nil}
797
- end
798
- tp instance_table, :id, :name, :group, :cloud, :type, :environment, :nodes, :connection, :status
799
- end
800
- print reset,"\n\n"
801
- end
802
-
803
- rescue RestClient::Exception => e
804
- print_rest_exception(e, options)
805
- exit 1
806
- end
807
- end
808
-
809
- def remove(args)
810
- options = {}
811
- query_params = {keepBackups: 'off', force: 'off'}
812
- optparse = OptionParser.new do|opts|
813
- opts.banner = "Usage: morpheus instances remove [name] [-fB]"
814
- opts.on( '-f', '--force', "Force Remove" ) do
815
- query_params[:force] = 'on'
816
- end
817
- opts.on( '-B', '--keep-backups', "Preserve copy of backups" ) do
818
- query_params[:keepBackups] = 'on'
819
- end
820
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
821
-
822
- end
823
- if args.count < 1
824
- puts "\n#{optparse}\n\n"
825
- exit 1
826
- end
827
- optparse.parse(args)
828
- connect(options)
829
- begin
830
- instance = find_instance_by_name_or_id(args[0])
831
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the instance '#{instance['name']}'?", options)
832
- exit 1
833
- end
834
- @instances_interface.destroy(instance['id'],query_params)
835
- list([])
836
- rescue RestClient::Exception => e
837
- print_rest_exception(e, options)
838
- exit 1
839
- end
840
- end
841
-
842
- def firewall_disable(args)
843
- options = {}
844
- optparse = OptionParser.new do|opts|
845
- opts.banner = "Usage: morpheus instances firewall-disable [name]"
846
- build_common_options(opts, options, [:json, :remote])
847
- end
848
- if args.count < 1
849
- puts "\n#{optparse.banner}\n\n"
850
- exit 1
851
- end
852
- optparse.parse(args)
853
- connect(options)
854
- begin
855
- instance = find_instance_by_name_or_id(args[0])
856
- @instances_interface.firewall_disable(instance['id'])
857
- security_groups([args[0]])
858
- rescue RestClient::Exception => e
859
- print_rest_exception(e, options)
860
- exit 1
861
- end
862
- end
863
-
864
- def firewall_enable(args)
865
- options = {}
866
- optparse = OptionParser.new do|opts|
867
- opts.banner = "Usage: morpheus instances firewall-enable [name]"
868
- build_common_options(opts, options, [:json, :remote])
869
- end
870
- if args.count < 1
871
- puts "\n#{optparse.banner}\n\n"
872
- exit 1
873
- end
874
- optparse.parse(args)
875
- connect(options)
876
- begin
877
- instance_results = @instances_interface.get({name: args[0]})
878
- if instance_results['instances'].empty?
879
- puts "Instance not found by name #{args[0]}"
880
- return
881
- end
882
- @instances_interface.firewall_enable(instance_results['instances'][0]['id'])
883
- security_groups([args[0]])
884
- rescue RestClient::Exception => e
885
- print_rest_exception(e, options)
886
- exit 1
887
- end
888
- end
889
-
890
- def security_groups(args)
891
- options = {}
892
- optparse = OptionParser.new do|opts|
893
- opts.banner = "Usage: morpheus instances security-groups [name]"
894
- build_common_options(opts, options, [:json, :remote])
895
- end
896
- if args.count < 1
897
- puts "\n#{optparse.banner}\n\n"
898
- exit 1
899
- end
900
- optparse.parse(args)
901
- connect(options)
902
- begin
903
- instance = find_instance_by_name_or_id(args[0])
904
- instance_id = instance['id']
905
- json_response = @instances_interface.security_groups(instance_id)
906
-
907
- securityGroups = json_response['securityGroups']
908
- print "\n" ,cyan, bold, "Morpheus Security Groups for Instance:#{instance_id}\n","==================", reset, "\n\n"
909
- print cyan, "Firewall Enabled=#{json_response['firewallEnabled']}\n\n"
910
- if securityGroups.empty?
911
- puts yellow,"No security groups currently applied.",reset
912
- else
913
- securityGroups.each do |securityGroup|
914
- print cyan, "= #{securityGroup['id']} (#{securityGroup['name']}) - (#{securityGroup['description']})\n"
915
- end
916
- end
917
- print reset,"\n\n"
918
-
919
- rescue RestClient::Exception => e
920
- print_rest_exception(e, options)
921
- exit 1
922
- end
923
- end
924
-
925
- def apply_security_groups(args)
926
- options = {}
927
- clear_or_secgroups_specified = false
928
- optparse = OptionParser.new do|opts|
929
- opts.banner = "Usage: morpheus instances apply-security-groups [name] [options]"
930
- opts.on( '-c', '--clear', "Clear all security groups" ) do
931
- options[:securityGroupIds] = []
932
- clear_or_secgroups_specified = true
933
- end
934
- opts.on( '-S', '--secgroups SECGROUPS', "Apply the specified comma separated security group ids" ) do |secgroups|
935
- options[:securityGroupIds] = secgroups.split(",")
936
- clear_or_secgroups_specified = true
937
- end
938
- build_common_options(opts, options, [:json, :remote])
939
- end
940
- if args.count < 1
941
- puts "\n#{optparse.banner}\n\n"
942
- exit 1
943
- end
944
- optparse.parse(args)
945
- connect(options)
946
-
947
- if !clear_or_secgroups_specified
948
- puts usage
949
- exit
950
- end
951
-
952
- begin
953
- instance = find_instance_by_name_or_id(args[0])
954
- @instances_interface.apply_security_groups(instance['id'], options)
955
- security_groups([args[0]])
956
- rescue RestClient::Exception => e
957
- print_rest_exception(e, options)
958
- exit 1
959
- end
960
- end
961
-
962
-
963
- def run_workflow(args)
964
- options = {}
965
-
966
- optparse = OptionParser.new do|opts|
967
- opts.banner = "Usage: morpheus instances run-workflow [INSTANCE] [name] [options]"
968
- build_common_options(opts, options, [:options, :json, :remote])
969
- end
970
- if args.count < 2
971
- puts "\n#{optparse}\n\n"
972
- exit 1
973
- end
974
-
975
- optparse.parse(args)
976
- connect(options)
977
- instance = find_instance_by_name_or_id(args[0])
978
- workflow = find_workflow_by_name(args[1])
979
- task_types = @tasks_interface.task_types()
980
- editable_options = []
981
- workflow['taskSetTasks'].sort{|a,b| a['taskOrder'] <=> b['taskOrder']}.each do |task_set_task|
982
- task_type_id = task_set_task['task']['taskType']['id']
983
- task_type = task_types['taskTypes'].find{ |current_task_type| current_task_type['id'] == task_type_id}
984
- task_opts = task_type['optionTypes'].select { |otype| otype['editable']}
985
- if !task_opts.nil? && !task_opts.empty?
986
- editable_options += task_opts.collect do |task_opt|
987
- new_task_opt = task_opt.clone
988
- new_task_opt['fieldContext'] = "#{task_set_task['id']}.#{new_task_opt['fieldContext']}"
989
- end
990
- end
991
- end
992
- params = options[:options] || {}
993
-
994
- if params.empty? && !editable_options.empty?
995
- puts "\n#{optparse.banner}\n\n"
996
- option_lines = editable_options.collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
997
- puts "\nAvailable Options:\n#{option_lines}\n\n"
998
- exit 1
999
- end
1000
-
1001
- workflow_payload = {taskSet: {"#{workflow['id']}" => params }}
1002
- begin
1003
-
1004
- json_response = @instances_interface.workflow(instance['id'],workflow['id'], workflow_payload)
1005
- if options[:json]
1006
- print JSON.pretty_generate(json_response)
1007
- print "\n"
1008
- else
1009
- puts "Running workflow..."
1010
- end
1011
- rescue RestClient::Exception => e
1012
- print_rest_exception(e, options)
1013
- exit 1
1014
- end
1015
- end
1016
-
1017
- private
1018
-
1019
- def find_instance_by_id(id)
1020
- instance_results = @instances_interface.get(id.to_i)
1021
- if instance_results['instance'].empty?
1022
- print_red_alert "Instance not found by id #{id}"
1023
- exit 1
1024
- end
1025
- instance = instance_results['instance']
1026
- # instance['stats'] = instance_results['stats'] || {}
1027
- # instance['loadBalancers'] = instance_results['loadBalancers'][0]['lbs'] || {}
1028
- return instance
1029
- end
1030
-
1031
- def find_instance_by_name(name)
1032
- instance_results = @instances_interface.get({name: name})
1033
- if instance_results['instances'].empty?
1034
- print_red_alert "Instance not found by name #{name}"
1035
- exit 1
1036
- end
1037
- instance = instance_results['instances'][0]
1038
- # instance['stats'] = instance_results['stats'][instance['id'].to_s] || {}
1039
- return instance
1040
- end
1041
-
1042
- def find_instance_by_name_or_id(val)
1043
- if val.to_s =~ /\A\d{1,}\Z/
1044
- return find_instance_by_id(val)
1045
- else
1046
- return find_instance_by_name(val)
1047
- end
1048
- end
1049
-
1050
- def find_workflow_by_name(name)
1051
- task_set_results = @task_sets_interface.get(name)
1052
- if !task_set_results['taskSets'].nil? && !task_set_results['taskSets'].empty?
1053
- return task_set_results['taskSets'][0]
1054
- else
1055
- print_red_alert "Workflow not found by name #{name}"
1056
- exit 1
1057
- end
1058
- end
159
+ end
160
+
161
+ def status_check(args)
162
+ out = ""
163
+ options = {}
164
+ optparse = OptionParser.new do|opts|
165
+ opts.banner = subcommand_usage("[name]")
166
+ build_common_options(opts, options, [:quiet, :json, :remote]) # no :dry_run, just do it man
167
+ end
168
+ optparse.parse!(args)
169
+ if args.count < 1
170
+ puts optparse
171
+ exit 1
172
+ end
173
+ connect(options)
174
+ # todo: just return status or maybe check if instance['status'] == args[0]
175
+ instance = find_instance_by_name_or_id(args[0])
176
+ exit_code = 0
177
+ if instance['status'].to_s.downcase != (args[1] || "running").to_s.downcase
178
+ exit_code = 1
179
+ end
180
+ if options[:json]
181
+ mock_json = {status: instance['status'], exit: exit_code}
182
+ out << JSON.pretty_generate(mock_json)
183
+ out << "\n"
184
+ elsif !options[:quiet]
185
+ out << cyan
186
+ out << "Status: #{format_instance_status(instance)}"
187
+ out << reset
188
+ out << "\n"
189
+ end
190
+ print out unless options[:quiet]
191
+ exit exit_code #return exit_code
192
+ end
193
+
194
+ def stats(args)
195
+ options = {}
196
+ optparse = OptionParser.new do|opts|
197
+ opts.banner = subcommand_usage("[name]")
198
+ build_common_options(opts, options, [:json, :dry_run, :remote])
199
+ end
200
+ optparse.parse!(args)
201
+ if args.count < 1
202
+ puts optparse
203
+ exit 1
204
+ end
205
+ connect(options)
206
+ begin
207
+ instance = find_instance_by_name_or_id(args[0])
208
+ if options[:dry_run]
209
+ print_dry_run @instances_interface.dry.get(instance['id'])
210
+ return
211
+ end
212
+ json_response = @instances_interface.get(instance['id'])
213
+ if options[:json]
214
+ print JSON.pretty_generate(json_response), "\n"
215
+ return
216
+ end
217
+ instance = json_response['instance']
218
+ stats = json_response['stats'] || {}
219
+ print "\n" ,cyan, bold, "Instance Stats: #{instance['name']} (#{instance['instanceType']['name']})\n","==================", "\n\n", reset, cyan
220
+ puts "Status: #{format_instance_status(instance)}"
221
+ print "\n"
222
+ if ((stats['maxMemory'].to_i != 0) || (stats['maxStorage'].to_i != 0))
223
+ # stats_map = {}
224
+ # stats_map[:memory] = "#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}"
225
+ # stats_map[:storage] = "#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}"
226
+ # stats_map[:cpu] = "#{stats['usedCpu'].to_f.round(2)}%"
227
+ # tp [stats_map], :memory,:storage,:cpu
228
+ # print "\n"
229
+ print_stats_usage(stats)
230
+ else
231
+ print cyan, "No data.", "\n", reset
232
+ end
233
+ print reset, "\n"
234
+ rescue RestClient::Exception => e
235
+ print_rest_exception(e, options)
236
+ exit 1
237
+ end
238
+ end
239
+
240
+ def console(args)
241
+ options = {}
242
+ optparse = OptionParser.new do|opts|
243
+ opts.banner = subcommand_usage("[name]")
244
+ opts.on( '-n', '--node NODE_ID', "Scope console to specific Container or VM" ) do |node_id|
245
+ options[:node_id] = node_id.to_i
246
+ end
247
+ # build_common_options(opts, options, [:list, :json, :dry_run, :remote])
248
+ end
249
+ optparse.parse!(args)
250
+ if args.count < 1
251
+ puts optparse
252
+ exit 1
253
+ end
254
+ connect(options)
255
+
256
+ begin
257
+ instance = find_instance_by_name_or_id(args[0])
258
+ link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/terminal/#{instance['id']}"
259
+ container_ids = instance['containers']
260
+ if options[:node_id] && container_ids.include?(options[:node_id])
261
+ link += "?containerId=#{options[:node_id]}"
262
+ end
263
+
264
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
265
+ system "start #{link}"
266
+ elsif RbConfig::CONFIG['host_os'] =~ /darwin/
267
+ system "open #{link}"
268
+ elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
269
+ system "xdg-open #{link}"
270
+ end
271
+ rescue RestClient::Exception => e
272
+ print_rest_exception(e, options)
273
+ exit 1
274
+ end
275
+ end
276
+
277
+ def logs(args)
278
+ options = {}
279
+ optparse = OptionParser.new do|opts|
280
+ opts.banner = subcommand_usage("[name]")
281
+ opts.on( '-n', '--node NODE_ID', "Scope logs to specific Container or VM" ) do |node_id|
282
+ options[:node_id] = node_id.to_i
283
+ end
284
+ build_common_options(opts, options, [:list, :json, :dry_run, :remote])
285
+ end
286
+ optparse.parse!(args)
287
+ if args.count < 1
288
+ puts optparse
289
+ exit 1
290
+ end
291
+ connect(options)
292
+ begin
293
+ instance = find_instance_by_name_or_id(args[0])
294
+ container_ids = instance['containers']
295
+ if options[:node_id] && container_ids.include?(options[:node_id])
296
+ container_ids = [options[:node_id]]
297
+ end
298
+ query_params = { max: options[:max] || 100, offset: options[:offset] || 0, query: options[:phrase]}
299
+ if options[:dry_run]
300
+ print_dry_run @logs_interface.dry.container_logs(container_ids, query_params)
301
+ return
302
+ end
303
+ logs = @logs_interface.container_logs(container_ids, query_params)
304
+ if options[:json]
305
+ puts logs
306
+ else
307
+ logs['data'].reverse.each do |log_entry|
308
+ log_level = ''
309
+ case log_entry['level']
310
+ when 'INFO'
311
+ log_level = "#{blue}#{bold}INFO#{reset}"
312
+ when 'DEBUG'
313
+ log_level = "#{white}#{bold}DEBUG#{reset}"
314
+ when 'WARN'
315
+ log_level = "#{yellow}#{bold}WARN#{reset}"
316
+ when 'ERROR'
317
+ log_level = "#{red}#{bold}ERROR#{reset}"
318
+ when 'FATAL'
319
+ log_level = "#{red}#{bold}FATAL#{reset}"
320
+ end
321
+ puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}"
322
+ end
323
+ print reset,"\n"
324
+ end
325
+ rescue RestClient::Exception => e
326
+ print_rest_exception(e, options)
327
+ exit 1
328
+ end
329
+ end
330
+
331
+ def get(args)
332
+ options = {}
333
+ optparse = OptionParser.new do|opts|
334
+ opts.banner = subcommand_usage("[name]")
335
+ build_common_options(opts, options, [:json, :dry_run, :remote])
336
+ end
337
+ optparse.parse!(args)
338
+ if args.count < 1
339
+ puts optparse
340
+ exit 1
341
+ end
342
+ connect(options)
343
+ begin
344
+ if options[:dry_run]
345
+ if args[0].to_s =~ /\A\d{1,}\Z/
346
+ print_dry_run @instances_interface.dry.get(args[0].to_i)
347
+ else
348
+ print_dry_run @instances_interface.dry.get({name:args[0]})
349
+ end
350
+ return
351
+ end
352
+ instance = find_instance_by_name_or_id(args[0])
353
+ json_response = @instances_interface.get(instance['id'])
354
+ if options[:json]
355
+ print JSON.pretty_generate(json_response), "\n"
356
+ return
357
+ end
358
+ instance = json_response['instance']
359
+ stats = json_response['stats'] || {}
360
+ # load_balancers = stats = json_response['loadBalancers'] || {}
361
+
362
+ connection_string = ''
363
+ if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
364
+ connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
365
+ end
366
+
367
+ print "\n" ,cyan, bold, "Instance Details\n","==================", reset, "\n\n"
368
+ print cyan
369
+ puts "ID: #{instance['id']}"
370
+ puts "Name: #{instance['name']}"
371
+ puts "Description: #{instance['description']}"
372
+ puts "Group: #{instance['group'] ? instance['group']['name'] : ''}"
373
+ puts "Cloud: #{instance['cloud'] ? instance['cloud']['name'] : ''}"
374
+ puts "Type: #{instance['instanceType']['name']}"
375
+ puts "Plan: #{instance['plan'] ? instance['plan']['name'] : ''}"
376
+ puts "Environment: #{instance['instanceContext']}"
377
+ puts "Nodes: #{instance['containers'] ? instance['containers'].count : 0}"
378
+ puts "Connection: #{connection_string}"
379
+ #puts "Account: #{instance['account'] ? instance['account']['name'] : ''}"
380
+ puts "Status: #{format_instance_status(instance)}"
381
+ if ((stats['maxMemory'].to_i != 0) || (stats['maxStorage'].to_i != 0))
382
+ print "\n"
383
+ # stats_map = {}
384
+ # stats_map[:memory] = "#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}"
385
+ # stats_map[:storage] = "#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}"
386
+ # stats_map[:cpu] = "#{stats['usedCpu'].to_f.round(2)}%"
387
+ # tp [stats_map], :memory,:storage,:cpu
388
+ print_stats_usage(stats)
389
+ else
390
+ # print cyan, "No data.", "\n", reset
391
+ end
392
+ print reset, "\n"
393
+
394
+ #puts instance
395
+ rescue RestClient::Exception => e
396
+ print_rest_exception(e, options)
397
+ exit 1
398
+ end
399
+ end
400
+
401
+ def backups(args)
402
+ options = {}
403
+ optparse = OptionParser.new do|opts|
404
+ opts.banner = subcommand_usage("[name]")
405
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
406
+ end
407
+ optparse.parse!(args)
408
+ if args.count < 1
409
+ puts optparse
410
+ exit 1
411
+ end
412
+ connect(options)
413
+ begin
414
+ instance = find_instance_by_name_or_id(args[0])
415
+ params = {}
416
+ if options[:dry_run]
417
+ print_dry_run @instances_interface.dry.backups(instance['id'], params)
418
+ return
419
+ end
420
+ json_response = @instances_interface.backups(instance['id'], params)
421
+ if options[:json]
422
+ print JSON.pretty_generate(json_response), "\n"
423
+ return
424
+ end
425
+ backups = json_response['backups']
426
+ stats = json_response['stats'] || {}
427
+ # load_balancers = stats = json_response['loadBalancers'] || {}
428
+
429
+ print "\n" ,cyan, bold, "Instance Backups\n","==================", reset, "\n\n"
430
+ print cyan
431
+ puts "ID: #{instance['id']}"
432
+ puts "Name: #{instance['name']}"
433
+ print "\n"
434
+ puts "Backups:"
435
+ backup_rows = backups.collect {|it| {id: it['id'], name: it['name'], dateCreated: it['dateCreated']} }
436
+ print cyan
437
+ tp backup_rows, [
438
+ :id,
439
+ :name,
440
+ {:dateCreated => {:display_name => "Date Created"} }
441
+ ]
442
+ print reset, "\n"
443
+ rescue RestClient::Exception => e
444
+ print_rest_exception(e, options)
445
+ exit 1
446
+ end
447
+ end
448
+
449
+ def envs(args)
450
+ options = {}
451
+ optparse = OptionParser.new do|opts|
452
+ opts.banner = subcommand_usage("[name]")
453
+ build_common_options(opts, options, [:json, :dry_run, :remote])
454
+ end
455
+ optparse.parse!(args)
456
+ if args.count < 1
457
+ puts optparse
458
+ exit 1
459
+ end
460
+ connect(options)
461
+ begin
462
+ instance = find_instance_by_name_or_id(args[0])
463
+ if options[:dry_run]
464
+ print_dry_run @instances_interface.dry.get_envs(instance['id'])
465
+ return
466
+ end
467
+ json_response = @instances_interface.get_envs(instance['id'])
468
+ if options[:json]
469
+ print JSON.pretty_generate(json_response), "\n"
470
+ return
471
+ end
472
+ print "\n" ,cyan, bold, "#{instance['name']} (#{instance['instanceType']['name']})\n","==================", "\n\n", reset, cyan
473
+ envs = json_response['envs'] || {}
474
+ if json_response['readOnlyEnvs']
475
+ envs += json_response['readOnlyEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") || v['masked'] ? "********" : v['value'], :export => true}}
476
+ end
477
+ tp envs, :name, :value, :export
478
+ print "\n" ,cyan, bold, "Imported Envs\n","==================", "\n\n", reset, cyan
479
+ imported_envs = json_response['importedEnvs'].map { |k,v| {:name => k, :value => k.downcase.include?("password") || v['masked'] ? "********" : v['value']}}
480
+ tp imported_envs
481
+ print reset, "\n"
482
+ rescue RestClient::Exception => e
483
+ print_rest_exception(e, options)
484
+ exit 1
485
+ end
486
+ end
487
+
488
+ def setenv(args)
489
+ options = {}
490
+
491
+ optparse = OptionParser.new do|opts|
492
+ opts.banner = subcommand_usage("[name] VAR VALUE [-e]")
493
+ opts.on( '-e', "Exportable" ) do |exportable|
494
+ options[:export] = exportable
495
+ end
496
+ opts.on( '-M', "Masked" ) do |masked|
497
+ options[:masked] = masked
498
+ end
499
+ build_common_options(opts, options, [:json, :dry_run, :remote, :quiet])
500
+ end
501
+ optparse.parse!(args)
502
+ if args.count < 3
503
+ puts optparse
504
+ exit 1
505
+ end
506
+ connect(options)
507
+ begin
508
+ instance = find_instance_by_name_or_id(args[0])
509
+ evar = {name: args[1], value: args[2], export: options[:export], masked: options[:masked]}
510
+ payload = {envs: [evar]}
511
+ if options[:dry_run]
512
+ print_dry_run @instances_interface.dry.create_env(instance['id'], payload)
513
+ return
514
+ end
515
+ json_response = @instances_interface.create_env(instance['id'], payload)
516
+ if options[:json]
517
+ print JSON.pretty_generate(json_response), "\n"
518
+ return
519
+ end
520
+ if !options[:quiet]
521
+ envs([args[0]])
522
+ end
523
+ rescue RestClient::Exception => e
524
+ print_rest_exception(e, options)
525
+ exit 1
526
+ end
527
+ end
528
+
529
+ def delenv(args)
530
+ options = {}
531
+ optparse = OptionParser.new do|opts|
532
+ opts.banner = subcommand_usage("[name] VAR")
533
+ build_common_options(opts, options, [:json, :dry_run, :remote])
534
+ end
535
+ optparse.parse!(args)
536
+ if args.count < 2
537
+ puts optparse
538
+ exit 1
539
+ end
540
+ connect(options)
541
+ begin
542
+ instance = find_instance_by_name_or_id(args[0])
543
+ if options[:dry_run]
544
+ print_dry_run @instances_interface.dry.del_env(instance['id'], args[1])
545
+ return
546
+ end
547
+ json_response = @instances_interface.del_env(instance['id'], args[1])
548
+ if options[:json]
549
+ print JSON.pretty_generate(json_response), "\n"
550
+ return
551
+ end
552
+ if !options[:quiet]
553
+ envs([args[0]])
554
+ end
555
+ rescue RestClient::Exception => e
556
+ print_rest_exception(e, options)
557
+ exit 1
558
+ end
559
+ end
560
+
561
+ def stop(args)
562
+ options = {}
563
+ optparse = OptionParser.new do|opts|
564
+ opts.banner = subcommand_usage("[name]")
565
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
566
+ end
567
+ optparse.parse!(args)
568
+ if args.count < 1
569
+ puts optparse
570
+ exit 1
571
+ end
572
+ connect(options)
573
+ begin
574
+ instance = find_instance_by_name_or_id(args[0])
575
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to stop this instance?", options)
576
+ exit 1
577
+ end
578
+ if options[:dry_run]
579
+ print_dry_run @instances_interface.dry.stop(instance['id'])
580
+ return
581
+ end
582
+ json_response = @instances_interface.stop(instance['id'])
583
+ if options[:json]
584
+ print JSON.pretty_generate(json_response)
585
+ print "\n"
586
+ end
587
+ return
588
+ rescue RestClient::Exception => e
589
+ print_rest_exception(e, options)
590
+ exit 1
591
+ end
592
+ end
593
+
594
+ def start(args)
595
+ options = {}
596
+ optparse = OptionParser.new do|opts|
597
+ opts.banner = subcommand_usage("[name]")
598
+ build_common_options(opts, options, [:json, :dry_run, :remote])
599
+ end
600
+ optparse.parse!(args)
601
+ if args.count < 1
602
+ puts optparse
603
+ exit 1
604
+ end
605
+ connect(options)
606
+ begin
607
+ instance = find_instance_by_name_or_id(args[0])
608
+ if options[:dry_run]
609
+ print_dry_run @instances_interface.dry.start(instance['id'])
610
+ return
611
+ end
612
+ json_response = @instances_interface.start(instance['id'])
613
+ if options[:json]
614
+ print JSON.pretty_generate(json_response)
615
+ print "\n"
616
+ end
617
+ return
618
+ rescue RestClient::Exception => e
619
+ print_rest_exception(e, options)
620
+ exit 1
621
+ end
622
+ end
1059
623
 
624
+ def restart(args)
625
+ options = {}
626
+ optparse = OptionParser.new do|opts|
627
+ opts.banner = subcommand_usage("[name]")
628
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
629
+ end
630
+ optparse.parse!(args)
631
+ if args.count < 1
632
+ puts optparse
633
+ exit 1
634
+ end
635
+ connect(options)
636
+ begin
637
+ instance = find_instance_by_name_or_id(args[0])
638
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to restart this instance?", options)
639
+ exit 1
640
+ end
641
+ if options[:dry_run]
642
+ print_dry_run @instances_interface.dry.restart(instance['id'])
643
+ return
644
+ end
645
+ json_response = @instances_interface.restart(instance['id'])
646
+ if options[:json]
647
+ print JSON.pretty_generate(json_response)
648
+ print "\n"
649
+ end
650
+ return
651
+ rescue RestClient::Exception => e
652
+ print_rest_exception(e, options)
653
+ exit 1
654
+ end
655
+ end
656
+
657
+ def suspend(args)
658
+ options = {}
659
+ optparse = OptionParser.new do|opts|
660
+ opts.banner = subcommand_usage("[name]")
661
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
662
+ end
663
+ optparse.parse!(args)
664
+ if args.count < 1
665
+ puts optparse
666
+ exit 1
667
+ end
668
+ connect(options)
669
+ begin
670
+ instance = find_instance_by_name_or_id(args[0])
671
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to suspend this instance?", options)
672
+ exit 1
673
+ end
674
+ if options[:dry_run]
675
+ print_dry_run @instances_interface.dry.suspend(instance['id'])
676
+ return
677
+ end
678
+ json_response = @instances_interface.suspend(instance['id'])
679
+ if options[:json]
680
+ print JSON.pretty_generate(json_response)
681
+ print "\n"
682
+ end
683
+ return
684
+ rescue RestClient::Exception => e
685
+ print_rest_exception(e, options)
686
+ exit 1
687
+ end
688
+ end
689
+
690
+ def eject(args)
691
+ options = {}
692
+ optparse = OptionParser.new do|opts|
693
+ opts.banner = subcommand_usage("restart [name]")
694
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
695
+ end
696
+ optparse.parse!(args)
697
+ if args.count < 1
698
+ puts optparse
699
+ exit 1
700
+ end
701
+ connect(options)
702
+ begin
703
+ instance = find_instance_by_name_or_id(args[0])
704
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to eject this instance?", options)
705
+ exit 1
706
+ end
707
+ if options[:dry_run]
708
+ print_dry_run @instances_interface.dry.eject(instance['id'])
709
+ return
710
+ end
711
+ json_response = @instances_interface.eject(instance['id'])
712
+ if options[:json]
713
+ print JSON.pretty_generate(json_response)
714
+ print "\n"
715
+ end
716
+ return
717
+ rescue RestClient::Exception => e
718
+ print_rest_exception(e, options)
719
+ exit 1
720
+ end
721
+ end
722
+
723
+ def stop_service(args)
724
+ options = {}
725
+ optparse = OptionParser.new do|opts|
726
+ opts.banner = subcommand_usage("[name]")
727
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
728
+ end
729
+ optparse.parse!(args)
730
+ if args.count < 1
731
+ puts optparse
732
+ exit 1
733
+ end
734
+ connect(options)
735
+ begin
736
+ instance = find_instance_by_name_or_id(args[0])
737
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to stop this instance?", options)
738
+ exit 1
739
+ end
740
+ if options[:dry_run]
741
+ print_dry_run @instances_interface.dry.stop(instance['id'],false)
742
+ return
743
+ end
744
+ json_response = @instances_interface.stop(instance['id'],false)
745
+ if options[:json]
746
+ print JSON.pretty_generate(json_response)
747
+ print "\n"
748
+ else
749
+ puts "Stopping service on #{args[0]}"
750
+ end
751
+ return
752
+ rescue RestClient::Exception => e
753
+ print_rest_exception(e, options)
754
+ exit 1
755
+ end
756
+ end
757
+
758
+ def start_service(args)
759
+ options = {}
760
+ optparse = OptionParser.new do|opts|
761
+ opts.banner = subcommand_usage("[name]")
762
+ build_common_options(opts, options, [:json, :dry_run, :remote])
763
+ end
764
+ optparse.parse!(args)
765
+ if args.count < 1
766
+ puts optparse
767
+ exit 1
768
+ end
769
+ connect(options)
770
+ begin
771
+ instance = find_instance_by_name_or_id(args[0])
772
+ if options[:dry_run]
773
+ print_dry_run @instances_interface.dry.start(instance['id'], false)
774
+ return
775
+ end
776
+ json_response = @instances_interface.start(instance['id'],false)
777
+ if options[:json]
778
+ print JSON.pretty_generate(json_response)
779
+ print "\n"
780
+ else
781
+ puts "Starting service on #{args[0]}"
782
+ end
783
+ return
784
+ rescue RestClient::Exception => e
785
+ print_rest_exception(e, options)
786
+ exit 1
787
+ end
788
+ end
789
+
790
+ def restart_service(args)
791
+ options = {}
792
+ optparse = OptionParser.new do|opts|
793
+ opts.banner = subcommand_usage("[name]")
794
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
795
+ end
796
+ optparse.parse!(args)
797
+ if args.count < 1
798
+ puts optparse
799
+ exit 1
800
+ end
801
+ connect(options)
802
+ begin
803
+ instance = find_instance_by_name_or_id(args[0])
804
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to restart this instance?", options)
805
+ exit 1
806
+ end
807
+ if options[:dry_run]
808
+ print_dry_run @instances_interface.dry.restart(instance['id'],false)
809
+ return
810
+ end
811
+ json_response = @instances_interface.restart(instance['id'],false)
812
+ if options[:json]
813
+ print JSON.pretty_generate(json_response)
814
+ print "\n"
815
+ else
816
+ puts "Restarting service on instance #{args[0]}"
817
+ end
818
+ return
819
+ rescue RestClient::Exception => e
820
+ print_rest_exception(e, options)
821
+ exit 1
822
+ end
823
+ end
824
+
825
+ def resize(args)
826
+ options = {}
827
+ optparse = OptionParser.new do|opts|
828
+ opts.banner = subcommand_usage("[name]")
829
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
830
+ end
831
+ optparse.parse!(args)
832
+ if args.count < 1
833
+ puts optparse
834
+ exit 1
835
+ end
836
+ connect(options)
837
+ begin
838
+ instance = find_instance_by_name_or_id(args[0])
839
+
840
+ group_id = instance['group']['id']
841
+ cloud_id = instance['cloud']['id']
842
+ layout_id = instance['layout']['id']
843
+
844
+ plan_id = instance['plan']['id']
845
+ payload = {
846
+ :instance => {:id => instance["id"]}
847
+ }
848
+
849
+ # avoid 500 error
850
+ # payload[:servicePlanOptions] = {}
851
+
852
+ puts "\nDue to limitations by most Guest Operating Systems, Disk sizes can only be expanded and not reduced.\nIf a smaller plan is selected, memory and CPU (if relevant) will be reduced but storage will not.\n\n"
853
+
854
+ # prompt for service plan
855
+ service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, layoutId: layout_id})
856
+ service_plans = service_plans_json["plans"]
857
+ service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"]} } # already sorted
858
+ service_plans_dropdown.each do |plan|
859
+ if plan['value'] && plan['value'].to_i == plan_id.to_i
860
+ plan['name'] = "#{plan['name']} (current)"
861
+ end
862
+ end
863
+ plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance'}],options[:options])
864
+ service_plan = service_plans.find {|sp| sp["id"] == plan_prompt['servicePlan'].to_i }
865
+ new_plan_id = service_plan["id"]
866
+ #payload[:servicePlan] = new_plan_id # ew, this api uses servicePlanId instead
867
+ #payload[:servicePlanId] = new_plan_id
868
+ payload[:instance][:plan] = {id: service_plan["id"]}
869
+
870
+ volumes_response = @instances_interface.volumes(instance['id'])
871
+ current_volumes = volumes_response['volumes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
872
+
873
+ # prompt for volumes
874
+ volumes = prompt_resize_volumes(current_volumes, service_plan, options)
875
+ if !volumes.empty?
876
+ payload[:volumes] = volumes
877
+ end
878
+
879
+ # only amazon supports this option
880
+ # for now, always do this
881
+ payload[:deleteOriginalVolumes] = true
882
+
883
+ if options[:dry_run]
884
+ print_dry_run @instances_interface.dry.resize(instance['id'], payload)
885
+ return
886
+ end
887
+ json_response = @instances_interface.resize(instance['id'], payload)
888
+ if options[:json]
889
+ print JSON.pretty_generate(json_response)
890
+ print "\n"
891
+ else
892
+ print_green_success "Resizing instance #{instance['name']}"
893
+ list([])
894
+ end
895
+ rescue RestClient::Exception => e
896
+ print_rest_exception(e, options)
897
+ exit 1
898
+ end
899
+ end
900
+
901
+ def backup(args)
902
+ options = {}
903
+ optparse = OptionParser.new do|opts|
904
+ opts.banner = subcommand_usage("[name]")
905
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
906
+ end
907
+ if args.count < 1
908
+ puts optparse
909
+ exit 1
910
+ end
911
+ optparse.parse!(args)
912
+ connect(options)
913
+ begin
914
+ instance = find_instance_by_name_or_id(args[0])
915
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to backup the instance '#{instance['name']}'?", options)
916
+ exit 1
917
+ end
918
+ if options[:dry_run]
919
+ print_dry_run @instances_interface.dry.backup(instance['id'])
920
+ return
921
+ end
922
+ json_response = @instances_interface.backup(instance['id'])
923
+ if options[:json]
924
+ print JSON.pretty_generate(json_response)
925
+ print "\n"
926
+ else
927
+ puts "Backup initiated."
928
+ end
929
+ return
930
+ rescue RestClient::Exception => e
931
+ print_rest_exception(e, options)
932
+ exit 1
933
+ end
934
+ end
935
+
936
+ def list(args)
937
+ options = {}
938
+ optparse = OptionParser.new do|opts|
939
+ opts.banner = subcommand_usage()
940
+ opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
941
+ options[:group] = val
942
+ end
943
+ opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
944
+ options[:cloud] = val
945
+ end
946
+ build_common_options(opts, options, [:list, :json, :dry_run, :remote])
947
+ end
948
+ optparse.parse!(args)
949
+ connect(options)
950
+ begin
951
+ params = {}
952
+ group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
953
+ if group
954
+ params['siteId'] = group['id']
955
+ end
956
+
957
+ # argh, this doesn't work because group_id is required for options/clouds
958
+ # cloud = options[:cloud] ? find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud]) : nil
959
+ cloud = options[:cloud] ? find_zone_by_name_or_id(nil, options[:cloud]) : nil
960
+ if cloud
961
+ params['zoneId'] = cloud['id']
962
+ end
963
+
964
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
965
+ params[k] = options[k] unless options[k].nil?
966
+ end
967
+
968
+ if options[:dry_run]
969
+ print_dry_run @instances_interface.dry.list(params)
970
+ return
971
+ end
972
+ json_response = @instances_interface.get(params)
973
+ if options[:json]
974
+ print JSON.pretty_generate(json_response)
975
+ print "\n"
976
+ else
977
+ instances = json_response['instances']
978
+
979
+ title = "Morpheus Instances"
980
+ subtitles = []
981
+ if group
982
+ subtitles << "Group: #{group['name']}".strip
983
+ end
984
+ if cloud
985
+ subtitles << "Cloud: #{cloud['name']}".strip
986
+ end
987
+ if params[:phrase]
988
+ subtitles << "Search: #{params[:phrase]}".strip
989
+ end
990
+ subtitle = subtitles.join(', ')
991
+ print "\n" ,cyan, bold, title, (subtitle.empty? ? "" : " - #{subtitle}"), "\n", "==================", reset, "\n\n"
992
+ if instances.empty?
993
+ puts yellow,"No instances found.",reset
994
+ else
995
+ print_instances_table(instances)
996
+ print_results_pagination(json_response)
997
+ end
998
+ print reset,"\n"
999
+ end
1000
+ rescue RestClient::Exception => e
1001
+ print_rest_exception(e, options)
1002
+ exit 1
1003
+ end
1004
+ end
1005
+
1006
+ def remove(args)
1007
+ options = {}
1008
+ query_params = {keepBackups: 'off', force: 'off'}
1009
+ optparse = OptionParser.new do|opts|
1010
+ opts.banner = subcommand_usage("[name] [-fB]")
1011
+ opts.on( '-f', '--force', "Force Remove" ) do
1012
+ query_params[:force] = 'on'
1013
+ end
1014
+ opts.on( '-B', '--keep-backups', "Preserve copy of backups" ) do
1015
+ query_params[:keepBackups] = 'on'
1016
+ end
1017
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
1018
+
1019
+ end
1020
+ if args.count < 1
1021
+ puts "\n#{optparse}\n\n"
1022
+ exit 1
1023
+ end
1024
+ optparse.parse!(args)
1025
+ connect(options)
1026
+ begin
1027
+ instance = find_instance_by_name_or_id(args[0])
1028
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the instance '#{instance['name']}'?", options)
1029
+ exit 1
1030
+ end
1031
+ if options[:dry_run]
1032
+ print_dry_run @instances_interface.dry.destroy(instance['id'],query_params)
1033
+ return
1034
+ end
1035
+ json_response = @instances_interface.destroy(instance['id'],query_params)
1036
+ if options[:json]
1037
+ print JSON.pretty_generate(json_response)
1038
+ print "\n"
1039
+ elsif !options[:quiet]
1040
+ list([])
1041
+ end
1042
+ rescue RestClient::Exception => e
1043
+ print_rest_exception(e, options)
1044
+ exit 1
1045
+ end
1046
+ end
1047
+
1048
+ def firewall_disable(args)
1049
+ options = {}
1050
+ optparse = OptionParser.new do|opts|
1051
+ opts.banner = subcommand_usage("[name]")
1052
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
1053
+ end
1054
+ if args.count < 1
1055
+ puts optparse
1056
+ exit 1
1057
+ end
1058
+ optparse.parse!(args)
1059
+ connect(options)
1060
+ begin
1061
+ instance = find_instance_by_name_or_id(args[0])
1062
+ if options[:dry_run]
1063
+ print_dry_run @instances_interface.dry.firewall_disable(instance['id'])
1064
+ return
1065
+ end
1066
+ json_response = @instances_interface.firewall_disable(instance['id'])
1067
+ if options[:json]
1068
+ print JSON.pretty_generate(json_response)
1069
+ print "\n"
1070
+ elsif !options[:quiet]
1071
+ security_groups([args[0]])
1072
+ end
1073
+ rescue RestClient::Exception => e
1074
+ print_rest_exception(e, options)
1075
+ exit 1
1076
+ end
1077
+ end
1078
+
1079
+ def firewall_enable(args)
1080
+ options = {}
1081
+ optparse = OptionParser.new do|opts|
1082
+ opts.banner = subcommand_usage("[name]")
1083
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
1084
+ end
1085
+ if args.count < 1
1086
+ puts optparse
1087
+ exit 1
1088
+ end
1089
+ optparse.parse!(args)
1090
+ connect(options)
1091
+ begin
1092
+ instance = find_instance_by_name_or_id(args[0])
1093
+ if options[:dry_run]
1094
+ print_dry_run @instances_interface.dry.firewall_enable(instance['id'])
1095
+ return
1096
+ end
1097
+ json_response = @instances_interface.firewall_enable(instance['id'])
1098
+ if options[:json]
1099
+ print JSON.pretty_generate(json_response)
1100
+ print "\n"
1101
+ elsif !options[:quiet]
1102
+ security_groups([args[0]])
1103
+ end
1104
+ rescue RestClient::Exception => e
1105
+ print_rest_exception(e, options)
1106
+ exit 1
1107
+ end
1108
+ end
1109
+
1110
+ def security_groups(args)
1111
+ options = {}
1112
+ optparse = OptionParser.new do|opts|
1113
+ opts.banner = subcommand_usage("[name]")
1114
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1115
+ end
1116
+ if args.count < 1
1117
+ puts optparse
1118
+ exit 1
1119
+ end
1120
+ optparse.parse!(args)
1121
+ connect(options)
1122
+ begin
1123
+ instance = find_instance_by_name_or_id(args[0])
1124
+ if options[:dry_run]
1125
+ print_dry_run @instances_interface.dry.security_groups(instance['id'])
1126
+ return
1127
+ end
1128
+ json_response = @instances_interface.security_groups(instance['id'])
1129
+ if options[:json]
1130
+ print JSON.pretty_generate(json_response)
1131
+ print "\n"
1132
+ return
1133
+ end
1134
+ securityGroups = json_response['securityGroups']
1135
+ print "\n" ,cyan, bold, "Morpheus Security Groups for Instance: #{instance['name']}\n","==================", reset, "\n\n"
1136
+ print cyan, "Firewall Enabled=#{json_response['firewallEnabled']}\n\n"
1137
+ if securityGroups.empty?
1138
+ puts yellow,"No security groups currently applied.",reset
1139
+ else
1140
+ securityGroups.each do |securityGroup|
1141
+ print cyan, "= #{securityGroup['id']} (#{securityGroup['name']}) - (#{securityGroup['description']})\n"
1142
+ end
1143
+ end
1144
+ print reset,"\n"
1145
+
1146
+ rescue RestClient::Exception => e
1147
+ print_rest_exception(e, options)
1148
+ exit 1
1149
+ end
1150
+ end
1151
+
1152
+ def apply_security_groups(args)
1153
+ options = {}
1154
+ security_group_ids = nil
1155
+ clear_or_secgroups_specified = false
1156
+ optparse = OptionParser.new do|opts|
1157
+ opts.banner = subcommand_usage("[name] [-S] [-c]")
1158
+ opts.on( '-S', '--secgroups SECGROUPS', "Apply the specified comma separated security group ids" ) do |secgroups|
1159
+ security_group_ids = secgroups.split(",")
1160
+ clear_or_secgroups_specified = true
1161
+ end
1162
+ opts.on( '-c', '--clear', "Clear all security groups" ) do
1163
+ security_group_ids = []
1164
+ clear_or_secgroups_specified = true
1165
+ end
1166
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
1167
+ end
1168
+ optparse.parse!(args)
1169
+ if args.count < 1
1170
+ puts optparse
1171
+ exit 1
1172
+ end
1173
+ if !clear_or_secgroups_specified
1174
+ puts optparse
1175
+ exit
1176
+ end
1177
+ connect(options)
1178
+ begin
1179
+ instance = find_instance_by_name_or_id(args[0])
1180
+ payload = {securityGroupIds: security_group_ids}
1181
+ if options[:dry_run]
1182
+ print_dry_run @instances_interface.dry.apply_security_groups(instance['id'], payload)
1183
+ return
1184
+ end
1185
+ json_response = @instances_interface.apply_security_groups(instance['id'], payload)
1186
+ if options[:json]
1187
+ print JSON.pretty_generate(json_response)
1188
+ print "\n"
1189
+ return
1190
+ end
1191
+ if !options[:quiet]
1192
+ security_groups([args[0]])
1193
+ end
1194
+ rescue RestClient::Exception => e
1195
+ print_rest_exception(e, options)
1196
+ exit 1
1197
+ end
1198
+ end
1199
+
1200
+
1201
+ def run_workflow(args)
1202
+ options = {}
1203
+ optparse = OptionParser.new do|opts|
1204
+ opts.banner = subcommand_usage("[name] [workflow] [options]")
1205
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
1206
+ end
1207
+ if args.count < 2
1208
+ puts "\n#{optparse}\n\n"
1209
+ exit 1
1210
+ end
1211
+ optparse.parse!(args)
1212
+ connect(options)
1213
+ instance = find_instance_by_name_or_id(args[0])
1214
+ workflow = find_workflow_by_name(args[1])
1215
+ task_types = @tasks_interface.task_types()
1216
+ editable_options = []
1217
+ workflow['taskSetTasks'].sort{|a,b| a['taskOrder'] <=> b['taskOrder']}.each do |task_set_task|
1218
+ task_type_id = task_set_task['task']['taskType']['id']
1219
+ task_type = task_types['taskTypes'].find{ |current_task_type| current_task_type['id'] == task_type_id}
1220
+ task_opts = task_type['optionTypes'].select { |otype| otype['editable']}
1221
+ if !task_opts.nil? && !task_opts.empty?
1222
+ editable_options += task_opts.collect do |task_opt|
1223
+ new_task_opt = task_opt.clone
1224
+ new_task_opt['fieldContext'] = "#{task_set_task['id']}.#{new_task_opt['fieldContext']}"
1225
+ end
1226
+ end
1227
+ end
1228
+ params = options[:options] || {}
1229
+
1230
+ if params.empty? && !editable_options.empty?
1231
+ puts optparse
1232
+ option_lines = editable_options.collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
1233
+ puts "\nAvailable Options:\n#{option_lines}\n\n"
1234
+ exit 1
1235
+ end
1236
+
1237
+ workflow_payload = {taskSet: {"#{workflow['id']}" => params }}
1238
+ begin
1239
+ if options[:dry_run]
1240
+ print_dry_run @instances_interface.dry.workflow(instance['id'],workflow['id'], workflow_payload)
1241
+ return
1242
+ end
1243
+ json_response = @instances_interface.workflow(instance['id'],workflow['id'], workflow_payload)
1244
+ if options[:json]
1245
+ print JSON.pretty_generate(json_response)
1246
+ print "\n"
1247
+ else
1248
+ puts "Running workflow..."
1249
+ end
1250
+ rescue RestClient::Exception => e
1251
+ print_rest_exception(e, options)
1252
+ exit 1
1253
+ end
1254
+ end
1255
+
1256
+ def import_snapshot(args)
1257
+ options = {}
1258
+ storage_provider_id = nil
1259
+ optparse = OptionParser.new do|opts|
1260
+ opts.banner = subcommand_usage("[name]")
1261
+ opts.on("--storage-provider ID", String, "Optional storage provider") do |val|
1262
+ storage_provider_id = val
1263
+ end
1264
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
1265
+ end
1266
+ if args.count < 1
1267
+ puts optparse
1268
+ exit 1
1269
+ end
1270
+ optparse.parse!(args)
1271
+ connect(options)
1272
+ begin
1273
+ instance = find_instance_by_name_or_id(args[0])
1274
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to import a snapshot of the instance '#{instance['name']}'?", options)
1275
+ exit 1
1276
+ end
1277
+
1278
+ payload = {}
1279
+
1280
+ # Prompt for Storage Provider, use default value.
1281
+ begin
1282
+ options[:options] ||= {}
1283
+ options[:options]['storageProviderId'] = storage_provider_id if storage_provider_id
1284
+ storage_provider_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'storageProviderId', 'type' => 'select', 'fieldLabel' => 'Storage Provider', 'optionSource' => 'storageProviders', 'required' => false, 'description' => 'Select Storage Provider.'}], options[:options], @api_client, {})
1285
+ if !storage_provider_prompt['storageProviderId'].empty?
1286
+ payload['storageProviderId'] = storage_provider_prompt['storageProviderId']
1287
+ end
1288
+ rescue RestClient::Exception => e
1289
+ puts "Failed to load storage providers"
1290
+ #print_rest_exception(e, options)
1291
+ exit 1
1292
+ end
1293
+
1294
+ if options[:dry_run]
1295
+ print_dry_run @instances_interface.dry.import_snapshot(instance['id'], payload)
1296
+ return
1297
+ end
1298
+ json_response = @instances_interface.import_snapshot(instance['id'], payload)
1299
+ if options[:json]
1300
+ print JSON.pretty_generate(json_response)
1301
+ print "\n"
1302
+ else
1303
+ puts "Snapshot import initiated."
1304
+ end
1305
+ return
1306
+ rescue RestClient::Exception => e
1307
+ print_rest_exception(e, options)
1308
+ exit 1
1309
+ end
1310
+ end
1311
+
1312
+ # check the instance
1313
+ def check_status
1314
+ end
1315
+
1316
+ private
1317
+ def find_zone_by_name_or_id(group_id, val)
1318
+ zone = nil
1319
+ if val.to_s =~ /\A\d{1,}\Z/
1320
+ json_results = @clouds_interface.get(val.to_i)
1321
+ zone = json_results['zone']
1322
+ if zone.nil?
1323
+ print_red_alert "Cloud not found by id #{val}"
1324
+ exit 1
1325
+ end
1326
+ else
1327
+ json_results = @clouds_interface.get({groupId: group_id, name: val})
1328
+ zone = json_results['zones'] ? json_results['zones'][0] : nil
1329
+ if zone.nil?
1330
+ print_red_alert "Cloud not found by name #{val}"
1331
+ exit 1
1332
+ end
1333
+ end
1334
+ return zone
1335
+ end
1336
+
1337
+ def find_workflow_by_name(name)
1338
+ task_set_results = @task_sets_interface.get(name)
1339
+ if !task_set_results['taskSets'].nil? && !task_set_results['taskSets'].empty?
1340
+ return task_set_results['taskSets'][0]
1341
+ else
1342
+ print_red_alert "Workflow not found by name #{name}"
1343
+ exit 1
1344
+ end
1345
+ end
1346
+
1347
+ def print_instances_table(instances, opts={})
1348
+ table_color = opts[:color] || cyan
1349
+ rows = instances.collect do |instance|
1350
+ connection_string = ''
1351
+ if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
1352
+ connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
1353
+ end
1354
+ {
1355
+ id: instance['id'],
1356
+ name: instance['name'],
1357
+ connection: connection_string,
1358
+ environment: instance['instanceContext'],
1359
+ nodes: instance['containers'].count,
1360
+ status: format_instance_status(instance, table_color),
1361
+ type: instance['instanceType']['name'],
1362
+ group: !instance['group'].nil? ? instance['group']['name'] : nil,
1363
+ cloud: !instance['cloud'].nil? ? instance['cloud']['name'] : nil
1364
+ }
1365
+ end
1366
+
1367
+ print table_color
1368
+ tp rows, :id, :name, :group, :cloud, :type, :environment, :nodes, :connection, :status
1369
+ print reset
1370
+ end
1371
+
1372
+ def format_instance_status(instance, return_color=cyan)
1373
+ out = ""
1374
+ status_string = instance['status']
1375
+ if status_string == 'running'
1376
+ out << "#{green}#{status_string.upcase}#{return_color}"
1377
+ elsif status_string == 'stopped' or status_string == 'failed'
1378
+ out << "#{red}#{status_string.upcase}#{return_color}"
1379
+ elsif status_string == 'unknown'
1380
+ out << "#{white}#{status_string.upcase}#{return_color}"
1381
+ else
1382
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
1383
+ end
1384
+ out
1385
+ end
1060
1386
  end