manageiq-appliance_console 6.1.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34445fe4cc1b697688c30f689ed12499635959a5ad428763f2cfaac48a9f64c9
4
- data.tar.gz: ed7530438fc6d4cf6d143e99bee966a890af509bdcb065831db8897b995a7fa8
3
+ metadata.gz: 797309808ab952ceb8d17fc6fab131bf79b05f368880f888a01e107e093feca5
4
+ data.tar.gz: d4070fb3ca9be9277bb5cafefd6e873431596c034932f903ed33ee49459cbcbb
5
5
  SHA512:
6
- metadata.gz: a35d79649faa4a600b16959312a35468737a49f106524a510ee6d917154eb736547980a5597edc853b39325730ec3eece30efd4ed7375717525f5ad56ae3191e
7
- data.tar.gz: 537c818711a763daa128491cf61895fac0646a568d7b9f39d7701707ecfcef33272d1f0cb7347290f1f61e10eb4cb7729ad2cadd18556ab5c9dd8888c7104842
6
+ metadata.gz: 76b149807bed8d49aa84ca00b422b7d57ad4db10df43b50e83a768f32a7d826d751d25d8f56fb670a775589d8848524f10cac696f4d46a3039900fe11f3e8cb9
7
+ data.tar.gz: 97439d9affd187790f55f0340b0750f975acff4d69306097869f67f98b5e60ddaccaf3bce429382fa9d5e54819474ef5f17f7c609cca4dc1d43731a71d406bdf
@@ -38,7 +38,23 @@ module ApplianceConsole
38
38
  end
39
39
 
40
40
  def database?
41
- options[:standalone] || hostname
41
+ (options[:standalone] || hostname) && !database_admin?
42
+ end
43
+
44
+ def database_admin?
45
+ db_dump? || db_backup? || db_restore?
46
+ end
47
+
48
+ def db_dump?
49
+ options[:dump]
50
+ end
51
+
52
+ def db_backup?
53
+ options[:backup]
54
+ end
55
+
56
+ def db_restore?
57
+ options[:restore]
42
58
  end
43
59
 
44
60
  def local_database?
@@ -139,68 +155,72 @@ module ApplianceConsole
139
155
  self.options = Optimist.options(args) do
140
156
  banner "Usage: appliance_console_cli [options]"
141
157
 
142
- opt :host, "/etc/hosts name", :type => :string, :short => 'H'
143
- opt :region, "Region Number", :type => :integer, :short => "r"
144
- opt :internal, "Internal Database", :short => 'i'
145
- opt :hostname, "Database Hostname", :type => :string, :short => 'h'
146
- opt :port, "Database Port", :type => :integer, :default => 5432
147
- opt :username, "Database Username", :type => :string, :short => 'U', :default => "root"
148
- opt :password, "Database Password", :type => :string, :short => "p"
149
- opt :dbname, "Database Name", :type => :string, :short => "d", :default => "vmdb_production"
150
- opt :standalone, "Run this server as a standalone database server", :type => :bool, :short => 'S'
151
- opt :key, "Create encryption key", :type => :boolean, :short => "k"
152
- opt :fetch_key, "SSH host with encryption key", :type => :string, :short => "K"
153
- opt :force_key, "Forcefully create encryption key", :type => :boolean, :short => "f"
154
- opt :sshlogin, "SSH login", :type => :string, :default => "root"
155
- opt :sshpassword, "SSH password", :type => :string
156
- opt :replication, "Configure database replication as primary or standby", :type => :string, :short => :none
157
- opt :primary_host, "Primary database host IP address", :type => :string, :short => :none
158
- opt :standby_host, "Standby database host IP address", :type => :string, :short => :none
159
- opt :auto_failover, "Configure Replication Manager (repmgrd) for automatic failover", :type => :bool, :short => :none
160
- opt :cluster_node_number, "Database unique cluster node number", :type => :integer, :short => :none
161
- opt :verbose, "Verbose", :type => :boolean, :short => "v"
162
- opt :dbdisk, "Database Disk Path", :type => :string
163
- opt :logdisk, "Log Disk Path", :type => :string
164
- opt :tmpdisk, "Temp storage Disk Path", :type => :string
165
- opt :uninstall_ipa, "Uninstall IPA Client", :type => :boolean, :default => false
166
- opt :ipaserver, "IPA Server FQDN", :type => :string
167
- opt :ipaprincipal, "IPA Server principal", :type => :string, :default => "admin"
168
- opt :ipapassword, "IPA Server password", :type => :string
169
- opt :ipadomain, "IPA Server domain (optional)", :type => :string
170
- opt :iparealm, "IPA Server realm (optional)", :type => :string
171
- opt :ca, "CA name used for certmonger", :type => :string, :default => "ipa"
172
- opt :http_cert, "install certs for http server", :type => :boolean
173
- opt :extauth_opts, "External Authentication Options", :type => :string
174
- opt :saml_config, "Configure Appliance for SAML Authentication", :type => :boolean, :default => false
175
- opt :saml_client_host, "Optional Appliance host used for SAML registration", :type => :string
176
- opt :saml_idp_metadata, "The file path or URL of the SAML IDP Metadata", :type => :string
177
- opt :saml_enable_sso, "Optionally enable SSO with SAML Authentication", :type => :boolean, :default => false
178
- opt :saml_unconfig, "Unconfigure Appliance SAML Authentication", :type => :boolean, :default => false
179
- opt :oidc_config, "Configure Appliance for OpenID-Connect Authentication", :type => :boolean, :default => false
180
- opt :oidc_url, "The OpenID-Connect Provider URL", :type => :string
181
- opt :oidc_client_host, "Optional Appliance host used for OpenID-Connect Authentication", :type => :string
182
- opt :oidc_client_id, "The OpenID-Connect Provider Client ID", :type => :string
183
- opt :oidc_client_secret, "The OpenID-Connect Provider Client Secret", :type => :string
184
- opt :oidc_insecure, "OpenID-Connect Insecure No SSL Verify (development)", :type => :boolean, :default => false
185
- opt :oidc_introspection_endpoint, "The OpenID-Connect Provider Introspect Endpoint", :type => :string
186
- opt :oidc_enable_sso, "Optionally enable SSO with OpenID-Connect Authentication", :type => :boolean, :default => false
187
- opt :oidc_unconfig, "Unconfigure Appliance OpenID-Connect Authentication", :type => :boolean, :default => false
188
- opt :server, "{start|stop|restart} actions on evmserverd Server", :type => :string
189
- opt :openscap, "Setup OpenScap", :type => :boolean, :default => false
190
- opt :message_server_config, "Subcommand to Configure Appliance as a Kafka Message Server", :type => :boolean, :default => false
191
- opt :message_server_unconfig, "Subcommand to Unconfigure Appliance as a Kafka Message Server", :type => :boolean, :default => false
192
- opt :message_client_config, "Subcommand to Configure Appliance as a Kafka Message Client", :type => :boolean, :default => false
193
- opt :message_client_unconfig, "Subcommand to Unconfigure Appliance as a Kafka Message Client", :type => :boolean, :default => false
194
- opt :message_keystore_username, "Message Server Keystore Username", :type => :string
195
- opt :message_keystore_password, "Message Server Keystore Password", :type => :string
196
- opt :message_server_username, "Message Server Username", :type => :string
197
- opt :message_server_password, "Message Server password", :type => :string
198
- opt :message_server_port, "Message Server Port", :type => :integer
199
- opt :message_server_use_ipaddr, "Message Server Use Address", :type => :boolean, :default => false
200
- opt :message_server_host, "Message Server Hostname or IP Address", :type => :string
201
- opt :message_truststore_path_src, "Message Server Truststore Path", :type => :string
202
- opt :message_ca_cert_path_src, "Message Server CA Cert Path", :type => :string
203
- opt :message_persistent_disk, "Message Persistent Disk Path", :type => :string
158
+ opt :host, "/etc/hosts name", :type => :string, :short => 'H'
159
+ opt :region, "Region Number", :type => :integer, :short => "r"
160
+ opt :internal, "Internal Database", :short => 'i'
161
+ opt :hostname, "Database Hostname", :type => :string, :short => 'h'
162
+ opt :port, "Database Port", :type => :integer, :default => 5432
163
+ opt :username, "Database Username", :type => :string, :short => 'U', :default => "root"
164
+ opt :password, "Database Password", :type => :string, :short => "p"
165
+ opt :dbname, "Database Name", :type => :string, :short => "d", :default => "vmdb_production"
166
+ opt :local_file, "Source/Destination file for DB dump/backup/restore", :type => :string, :shoft => "l"
167
+ opt :dump, "Perform a pg-dump"
168
+ opt :backup, "Perform a pg-basebackup"
169
+ opt :restore, "Restore a database dump/backup"
170
+ opt :standalone, "Run this server as a standalone database server", :type => :bool, :short => 'S'
171
+ opt :key, "Create encryption key", :type => :boolean, :short => "k"
172
+ opt :fetch_key, "SSH host with encryption key", :type => :string, :short => "K"
173
+ opt :force_key, "Forcefully create encryption key", :type => :boolean, :short => "f"
174
+ opt :sshlogin, "SSH login", :type => :string, :default => "root"
175
+ opt :sshpassword, "SSH password", :type => :string
176
+ opt :replication, "Configure database replication as primary or standby", :type => :string, :short => :none
177
+ opt :primary_host, "Primary database host IP address", :type => :string, :short => :none
178
+ opt :standby_host, "Standby database host IP address", :type => :string, :short => :none
179
+ opt :auto_failover, "Configure Replication Manager (repmgrd) for automatic failover", :type => :bool, :short => :none
180
+ opt :cluster_node_number, "Database unique cluster node number", :type => :integer, :short => :none
181
+ opt :verbose, "Verbose", :type => :boolean, :short => "v"
182
+ opt :dbdisk, "Database Disk Path", :type => :string
183
+ opt :logdisk, "Log Disk Path", :type => :string
184
+ opt :tmpdisk, "Temp storage Disk Path", :type => :string
185
+ opt :uninstall_ipa, "Uninstall IPA Client", :type => :boolean, :default => false
186
+ opt :ipaserver, "IPA Server FQDN", :type => :string
187
+ opt :ipaprincipal, "IPA Server principal", :type => :string, :default => "admin"
188
+ opt :ipapassword, "IPA Server password", :type => :string
189
+ opt :ipadomain, "IPA Server domain (optional)", :type => :string
190
+ opt :iparealm, "IPA Server realm (optional)", :type => :string
191
+ opt :ca, "CA name used for certmonger", :type => :string, :default => "ipa"
192
+ opt :http_cert, "install certs for http server", :type => :boolean
193
+ opt :extauth_opts, "External Authentication Options", :type => :string
194
+ opt :saml_config, "Configure Appliance for SAML Authentication", :type => :boolean, :default => false
195
+ opt :saml_client_host, "Optional Appliance host used for SAML registration", :type => :string
196
+ opt :saml_idp_metadata, "The file path or URL of the SAML IDP Metadata", :type => :string
197
+ opt :saml_enable_sso, "Optionally enable SSO with SAML Authentication", :type => :boolean, :default => false
198
+ opt :saml_unconfig, "Unconfigure Appliance SAML Authentication", :type => :boolean, :default => false
199
+ opt :oidc_config, "Configure Appliance for OpenID-Connect Authentication", :type => :boolean, :default => false
200
+ opt :oidc_url, "The OpenID-Connect Provider URL", :type => :string
201
+ opt :oidc_client_host, "Optional Appliance host used for OpenID-Connect Authentication", :type => :string
202
+ opt :oidc_client_id, "The OpenID-Connect Provider Client ID", :type => :string
203
+ opt :oidc_client_secret, "The OpenID-Connect Provider Client Secret", :type => :string
204
+ opt :oidc_insecure, "OpenID-Connect Insecure No SSL Verify (development)", :type => :boolean, :default => false
205
+ opt :oidc_introspection_endpoint, "The OpenID-Connect Provider Introspect Endpoint", :type => :string
206
+ opt :oidc_enable_sso, "Optionally enable SSO with OpenID-Connect Authentication", :type => :boolean, :default => false
207
+ opt :oidc_unconfig, "Unconfigure Appliance OpenID-Connect Authentication", :type => :boolean, :default => false
208
+ opt :server, "{start|stop|restart} actions on evmserverd Server", :type => :string
209
+ opt :openscap, "Setup OpenScap", :type => :boolean, :default => false
210
+ opt :message_server_config, "Subcommand to Configure Appliance as a Kafka Message Server", :type => :boolean, :default => false
211
+ opt :message_server_unconfig, "Subcommand to Unconfigure Appliance as a Kafka Message Server", :type => :boolean, :default => false
212
+ opt :message_client_config, "Subcommand to Configure Appliance as a Kafka Message Client", :type => :boolean, :default => false
213
+ opt :message_client_unconfig, "Subcommand to Unconfigure Appliance as a Kafka Message Client", :type => :boolean, :default => false
214
+ opt :message_keystore_username, "Message Server Keystore Username", :type => :string
215
+ opt :message_keystore_password, "Message Server Keystore Password", :type => :string
216
+ opt :message_server_username, "Message Server Username", :type => :string
217
+ opt :message_server_password, "Message Server password", :type => :string
218
+ opt :message_server_port, "Message Server Port", :type => :integer
219
+ opt :message_server_use_ipaddr, "Message Server Use Address", :type => :boolean, :default => false
220
+ opt :message_server_host, "Message Server Hostname or IP Address", :type => :string
221
+ opt :message_truststore_path_src, "Message Server Truststore Path", :type => :string
222
+ opt :message_ca_cert_path_src, "Message Server CA Cert Path", :type => :string
223
+ opt :message_persistent_disk, "Message Persistent Disk Path", :type => :string
204
224
  end
205
225
  Optimist.die :region, "needed when setting up a local database" if region_number_required? && options[:region].nil?
206
226
  Optimist.die "Supply only one of --message-server-host or --message-server-use-ipaddr=true" if both_host_and_use_ip_addr_specified?
@@ -218,11 +238,12 @@ module ApplianceConsole
218
238
  end
219
239
 
220
240
  def region_number_required?
221
- !options[:standalone] && local_database?
241
+ !options[:standalone] && local_database? && !database_admin?
222
242
  end
223
243
 
224
244
  def run
225
- Optimist.educate unless set_host? || key? || database? || tmp_disk? || log_disk? ||
245
+ Optimist.educate unless set_host? || key? || database? || db_dump? || db_backup? ||
246
+ db_restore? || tmp_disk? || log_disk? ||
226
247
  uninstall_ipa? || install_ipa? || certs? || extauth_opts? ||
227
248
  set_server_state? || set_replication? || openscap? ||
228
249
  saml_config? || saml_unconfig? ||
@@ -240,6 +261,9 @@ module ApplianceConsole
240
261
  create_key if key?
241
262
  set_db if database?
242
263
  set_replication if set_replication?
264
+ db_dump if db_dump?
265
+ db_backup if db_backup?
266
+ db_restore if db_restore?
243
267
  config_tmp_disk if tmp_disk?
244
268
  config_log_disk if log_disk?
245
269
  uninstall_ipa if uninstall_ipa?
@@ -344,6 +368,33 @@ module ApplianceConsole
344
368
  db_replication.activate
345
369
  end
346
370
 
371
+ def db_dump
372
+ PostgresAdmin.backup_pg_dump(extract_db_opts(options))
373
+ end
374
+
375
+ def db_backup
376
+ PostgresAdmin.backup(extract_db_opts(options))
377
+ end
378
+
379
+ def db_restore
380
+ PostgresAdmin.restore(extract_db_opts(options))
381
+ end
382
+
383
+ DB_OPT_KEYS = %i[dbname username password hostname port local_file].freeze
384
+ def extract_db_opts(options)
385
+ require 'manageiq/appliance_console/postgres_admin'
386
+
387
+ db_opts = {}
388
+
389
+ DB_OPT_KEYS.each { |k| db_opts[k] = options[k] if options[k] }
390
+
391
+ if db_dump? && options[:exclude_table_data]
392
+ db_opts[:exclude_table_data] = options[:exclude_table_data]
393
+ end
394
+
395
+ db_opts
396
+ end
397
+
347
398
  def key_configuration
348
399
  @key_configuration ||= KeyConfiguration.new(
349
400
  :action => options[:fetch_key] ? :fetch : :create,
@@ -23,22 +23,17 @@ module ManageIQ
23
23
 
24
24
  WARN
25
25
 
26
- attr_accessor :uri
27
- attr_reader :action, :backup_type, :task, :task_params, :delete_agree, :filename
26
+ attr_reader :action, :backup_type, :database_opts, :delete_agree, :filename
28
27
 
29
28
  def initialize(action = :restore, input = $stdin, output = $stdout)
30
29
  super(input, output)
31
30
 
32
- @action = action
33
- @task_params = []
31
+ @action = action
32
+ @database_opts = {:dbname => DatabaseConfiguration.database_name}
34
33
  end
35
34
 
36
- def local_backup?
37
- backup_type == "local".freeze
38
- end
39
-
40
- def object_store_backup?
41
- backup_type == "s3".freeze || backup_type == "swift".freeze
35
+ def uri
36
+ @database_opts[:local_file]
42
37
  end
43
38
 
44
39
  def ask_questions
@@ -51,7 +46,6 @@ module ManageIQ
51
46
  say(DB_DUMP_WARNING) if action == :dump
52
47
  ask_file_location
53
48
  ask_for_tables_to_exclude_in_dump
54
- ask_to_split_up_output
55
49
  end
56
50
 
57
51
  def activate
@@ -63,144 +57,11 @@ module ManageIQ
63
57
  end
64
58
 
65
59
  def ask_file_location
66
- @backup_type = ask_with_menu(*file_menu_args) do |menu|
67
- menu.choice(CANCEL) { |_| raise MiqSignalError }
68
- end
69
- if URI(backup_type).scheme
70
- ask_custom_file_options(backup_type)
71
- else
72
- # calling methods like ask_ftp_file_options and ask_s3_file_options
73
- send("ask_#{backup_type}_file_options")
74
- end
75
- end
76
-
77
- def ask_local_file_options
78
- @uri = just_ask(*filename_prompt_args)
79
- @task = "evm:db:#{action}:local"
80
- @task_params = ["--", {:local_file => uri}]
81
- end
82
-
83
- def ask_nfs_file_options
84
- @filename = just_ask(*filename_prompt_args) unless action == :restore
85
- @uri = ask_for_uri(*remote_file_prompt_args_for("nfs"))
86
- @task = "evm:db:#{action}:remote"
87
-
88
- params = {:uri => uri}
89
- params[:remote_file_name] = filename if filename
90
-
91
- @task_params = ["--", params]
92
- end
93
-
94
- def ask_smb_file_options
95
- @filename = just_ask(*filename_prompt_args) unless action == :restore
96
- @uri = ask_for_uri(*remote_file_prompt_args_for("smb"))
97
- user = just_ask(USER_PROMPT)
98
- pass = ask_for_password("password for #{user}")
99
-
100
- params = {
101
- :uri => uri,
102
- :uri_username => user,
103
- :uri_password => pass
104
- }
105
- params[:remote_file_name] = filename if filename
106
-
107
- @task = "evm:db:#{action}:remote"
108
- @task_params = ["--", params]
109
- end
110
-
111
- def ask_s3_file_options
112
- access_key_prompt = <<-PROMPT.strip_heredoc.chomp
113
- Access Key ID with access to this file.
114
- Example: 'amazon_aws_user'
115
- PROMPT
116
-
117
- @filename = just_ask(*filename_prompt_args) unless action == :restore
118
- @uri = ask_for_uri(*remote_file_prompt_args_for("s3"), :optional_path => true)
119
- region = just_ask("Amazon Region for database file", "us-east-1")
120
- user = just_ask(access_key_prompt)
121
- pass = ask_for_password("Secret Access Key for #{user}")
122
-
123
- params = {
124
- :uri => uri,
125
- :uri_username => user,
126
- :uri_password => pass,
127
- :aws_region => region
128
- }
129
- params[:remote_file_name] = filename if filename
130
-
131
- @task = "evm:db:#{action}:remote"
132
- @task_params = ["--", params]
133
- end
134
-
135
- def ask_ftp_file_options
136
- @filename = just_ask(*filename_prompt_args) unless action == :restore
137
- @uri = ask_for_uri(*remote_file_prompt_args_for("ftp"), :optional_path => true)
138
- user = just_ask(USER_PROMPT)
139
- pass = ask_for_password("password for #{user}")
140
-
141
- params = { :uri => uri }
142
- params[:uri_username] = user if user.present?
143
- params[:uri_password] = pass if pass.present?
144
- params[:remote_file_name] = filename if filename
145
-
146
- @task = "evm:db:#{action}:remote"
147
- @task_params = ["--", params]
148
- end
149
-
150
- def ask_custom_file_options(server_uri)
151
- hostname = URI(server_uri).host
152
- @filename = ask_custom_file_prompt(hostname)
153
- @uri = server_uri
154
-
155
- params = {:uri => uri, :remote_file_name => filename}
156
-
157
- if (custom_params = custom_endpoint_config_for(hostname))
158
- params.merge!(custom_params[:rake_options]) if custom_params[:rake_options]
159
- end
160
-
161
- @task = "evm:db:#{action}:remote"
162
- @task_params = ["--", params]
163
- end
164
-
165
- def ask_swift_file_options
166
- require 'uri'
167
- swift_user_prompt = <<-PROMPT.strip_heredoc.chomp
168
- User Name with access to this file.
169
- Example: 'openstack_user'
170
- PROMPT
171
-
172
- @filename = just_ask(*filename_prompt_args) { |q| q.readline = false } unless action == :restore
173
- @uri = URI(ask_for_uri(*remote_file_prompt_args_for("swift")) { |q| q.readline = false })
174
- @task = "evm:db:#{action}:remote"
175
- user = just_ask(swift_user_prompt) { |q| q.readline = false }
176
- pass = ask_for_password("password for #{user}") { |q| q.readline = false }
177
- @uri.query = swift_query_elements.join('&').presence
178
-
179
- params = {
180
- :uri => @uri.to_s,
181
- :uri_username => user,
182
- :uri_password => pass
183
- }
184
- params[:remote_file_name] = filename if filename
185
- @task = "evm:db:#{action}:remote"
186
- @task_params = ["--", params]
187
- end
188
-
189
- def swift_query_elements
190
- region = just_ask("OpenStack Swift Region") { |q| q.readline = false }
191
- @uri.port = just_ask("OpenStack Swift Port", "5000") { |q| q.readline = false }
192
- security_protocol = ask_with_menu(*security_protocol_menu_args)
193
- api_version = ask_with_menu(*api_version_menu_args) { |q| q.readline = false }
194
- domain_ident = just_ask("OpenStack V3 Domain Identifier") { |q| q.readline = false } if api_version == "v3"
195
- query_elements = []
196
- query_elements << "region=#{region}" if region.present?
197
- query_elements << "api_version=#{api_version}" if api_version.present?
198
- query_elements << "domain_id=#{domain_ident}" if domain_ident.present?
199
- query_elements << "security_protocol=#{security_protocol}" if security_protocol.present?
60
+ @database_opts[:local_file] = just_ask(*filename_prompt_args)
200
61
  end
201
62
 
202
63
  def ask_to_delete_backup_after_restore
203
- if action == :restore && local_backup?
64
+ if action == :restore
204
65
  say("The local database restore file is located at: '#{uri}'.\n")
205
66
  @delete_agree = agree("Should this file be deleted after completing the restore? (Y/N): ")
206
67
  end
@@ -222,20 +83,14 @@ module ManageIQ
222
83
  255,
223
84
  Float::INFINITY)
224
85
 
225
- @task_params.last[:"exclude-table-data"] = table_excludes
226
- end || true
227
- end
228
-
229
- def ask_to_split_up_output
230
- if action == :dump && should_split_output?
231
- @task_params.last[:byte_count] = ask_for_string("byte size to split by", "500M")
86
+ @database_opts[:exclude_table_data] = table_excludes
232
87
  end || true
233
88
  end
234
89
 
235
90
  def confirm_and_execute
236
91
  if allowed_to_execute?
237
92
  processing_message
238
- run_rake
93
+ run_action
239
94
  end
240
95
  press_any_key
241
96
  end
@@ -259,58 +114,18 @@ module ManageIQ
259
114
  end
260
115
  end
261
116
 
262
- def api_version_menu_args
263
- [
264
- "OpenStack API Version",
265
- [["Keystone v2".freeze, "v2".freeze], ["Keystone v3".freeze, "v3".freeze], ["None".freeze, nil]].freeze,
266
- ["Keystone v2".freeze, "v2".freeze],
267
- nil
268
- ]
269
- end
270
-
271
- def file_menu_args
272
- [
273
- action == :restore ? "Restore Database File Source" : "#{action.capitalize} Output File Destination",
274
- file_options,
275
- "local",
276
- nil
277
- ]
278
- end
279
-
280
- def security_protocol_menu_args
281
- [
282
- "OpenStack Security Protocol",
283
- [["SSL without validation".freeze, "ssl".freeze], ["SSL".freeze, "ssl-with-validation".freeze], ["Non-SSL".freeze, "non-ssl".freeze], ["None".freeze, nil]].freeze,
284
- ["Non-SSL".freeze, "non-ssl".freeze],
285
- nil
286
- ]
287
- end
288
-
289
117
  def setting_header
290
118
  say("#{I18n.t("advanced_settings.db#{action}")}\n\n")
291
119
  end
292
120
 
293
121
  private
294
122
 
295
- def ask_custom_file_prompt(hostname)
296
- prompts = custom_endpoint_config_for(hostname)
297
- prompt_text = prompts && prompts[:filename_text] || "Target filename for backup".freeze
298
- prompt_regex = prompts && prompts[:filename_validator]
299
- validator = prompt_regex ? ->(x) { x.to_s =~ /#{prompt_regex}/ } : ->(x) { x.to_s.present? }
300
- just_ask(prompt_text, nil, validator)
301
- end
302
-
303
123
  def skip_file_location?(hostname)
304
124
  config = custom_endpoint_config_for(hostname)
305
125
  return false unless config && config[:enabled_for].present?
306
126
  !Array(config[:enabled_for]).include?(action.to_s)
307
127
  end
308
128
 
309
- def custom_endpoint_config_for(hostname)
310
- # hostname has a period in it, so we need to look it up by [] instead of the traditional i18n method
311
- I18n.t("database_admin.prompts", :default => {})[hostname.to_sym]
312
- end
313
-
314
129
  def should_exclude_tables?
315
130
  ask_yn?("Would you like to exclude tables in the dump") do |q|
316
131
  q.readline = true
@@ -327,16 +142,12 @@ module ManageIQ
327
142
  return restore_prompt_args if action == :restore
328
143
  default = action == :dump ? DB_DEFAULT_DUMP_FILE : DB_RESTORE_FILE
329
144
  prompt = "location to save the #{action} file to"
330
- if object_store_backup?
331
- prompt = "name for the remote #{action} file"
332
- default = File.basename(default)
333
- end
334
145
  [prompt, default, nil, "file that exists"]
335
146
  end
336
147
 
337
148
  def restore_prompt_args
338
149
  default = DB_RESTORE_FILE
339
- validator = LOCAL_FILE_VALIDATOR if local_backup?
150
+ validator = LOCAL_FILE_VALIDATOR
340
151
  prompt = "location of the local restore file"
341
152
  [prompt, default, validator, "file that exists"]
342
153
  end
@@ -347,12 +158,12 @@ module ManageIQ
347
158
  else
348
159
  "location to save the remote #{action} file to"
349
160
  end
350
- prompt += "\nExample: #{sample_url(remote_type)}"
161
+ prompt += "\nExample: #{sample_url}"
351
162
  [prompt, remote_type]
352
163
  end
353
164
 
354
- def sample_url(scheme)
355
- I18n.t("database_admin.sample_url.#{scheme}")
165
+ def sample_url
166
+ I18n.t("database_admin.sample_url.nfs")
356
167
  end
357
168
 
358
169
  def processing_message
@@ -364,15 +175,33 @@ module ManageIQ
364
175
  say(msg)
365
176
  end
366
177
 
367
- def run_rake
368
- rake_success = ManageIQ::ApplianceConsole::Utilities.rake(task, task_params)
369
- if rake_success && action == :restore && delete_agree
178
+ def run_action
179
+ success = send(action)
180
+ if success && action == :restore && delete_agree
370
181
  say("\nRemoving the database restore file #{uri}...")
371
182
  File.delete(uri)
372
- elsif !rake_success
183
+ elsif !success
373
184
  say("\nDatabase #{action} failed. Check the logs for more information")
374
185
  end
375
186
  end
187
+
188
+ def backup
189
+ result = PostgresAdmin.backup(database_opts)
190
+ ManageIQ::ApplianceConsole.logger.info("[#{@database_opts[:dbname]}] database has been backed up to file: [#{uri}]")
191
+ result
192
+ end
193
+
194
+ def dump
195
+ result = PostgresAdmin.backup_pg_dump(database_opts)
196
+ ManageIQ::ApplianceConsole.logger.info("[#{@database_opts[:dbname]}] database has been dumped up to file: [#{uri}]")
197
+ result
198
+ end
199
+
200
+ def restore
201
+ result = PostgresAdmin.restore(database_opts.merge(:backup_type => backup_type))
202
+ ManageIQ::ApplianceConsole.logger.info("[#{@database_opts[:dbname]}] database has been restored from file: [#{uri}]")
203
+ result
204
+ end
376
205
  end
377
206
  end
378
207
  end
@@ -5,9 +5,13 @@ require 'manageiq-password'
5
5
  require 'pathname'
6
6
  require 'fileutils'
7
7
 
8
+ require_relative './manageiq_user_mixin'
9
+
8
10
  module ManageIQ
9
11
  module ApplianceConsole
10
12
  class DatabaseConfiguration
13
+ include ManageIQ::ApplianceConsole::ManageiqUserMixin
14
+
11
15
  attr_accessor :adapter, :host, :username, :database, :port, :region
12
16
  attr_reader :password
13
17
 
@@ -275,7 +279,10 @@ FRIENDLY
275
279
 
276
280
  def do_save(settings)
277
281
  require 'yaml'
278
- File.write(DB_YML, YAML.dump(settings))
282
+ File.open(DB_YML, "w") do |f|
283
+ f.write(YAML.dump(settings))
284
+ f.chown(manageiq_uid, manageiq_gid)
285
+ end
279
286
  end
280
287
 
281
288
  def initialize_from_hash(hash)
@@ -82,8 +82,7 @@ module ApplianceConsole
82
82
  say("\nUpdating external authentication options on appliance ...")
83
83
  params = update_hash.collect { |key, value| "#{key}=#{value}" }
84
84
  params = configure_provider_type!(params)
85
- result = ManageIQ::ApplianceConsole::Utilities.rake_run("evm:settings:set", params)
86
- raise parse_errors(result).join(', ') if result.failure?
85
+ ManageIQ::ApplianceConsole::Utilities.rake_run!("evm:settings:set", params)
87
86
  end
88
87
  end
89
88
 
@@ -152,17 +151,8 @@ module ApplianceConsole
152
151
 
153
152
  def load_current
154
153
  say("\nFetching external authentication options from appliance ...")
155
- result = ManageIQ::ApplianceConsole::Utilities.rake_run("evm:settings:get", EXT_AUTH_OPTIONS.keys)
156
-
157
- if result.success?
158
- return parse_response(result)
159
- else
160
- raise parse_errors(result).join(', ')
161
- end
162
- end
163
-
164
- def parse_errors(result)
165
- result.error.split("\n").collect { |line| line if line =~ /^error: /i }.compact
154
+ result = ManageIQ::ApplianceConsole::Utilities.rake_run!("evm:settings:get", EXT_AUTH_OPTIONS.keys)
155
+ parse_response(result)
166
156
  end
167
157
 
168
158
  def normalize_key(key)
@@ -1,6 +1,5 @@
1
1
  require "pathname"
2
2
  require "manageiq/appliance_console/postgres_admin"
3
- require "pg"
4
3
  require "linux_admin"
5
4
 
6
5
  module ManageIQ
@@ -149,14 +148,14 @@ module ApplianceConsole
149
148
  end
150
149
 
151
150
  def create_postgres_root_user
152
- with_pg_connection do |conn|
151
+ PostgresAdmin.with_pg_connection do |conn|
153
152
  esc_pass = conn.escape_string(password)
154
153
  conn.exec("CREATE ROLE #{username} WITH LOGIN CREATEDB SUPERUSER PASSWORD '#{esc_pass}'")
155
154
  end
156
155
  end
157
156
 
158
157
  def create_postgres_database
159
- with_pg_connection do |conn|
158
+ PostgresAdmin.with_pg_connection do |conn|
160
159
  conn.exec("CREATE DATABASE #{database} OWNER #{username} ENCODING 'utf8'")
161
160
  end
162
161
  end
@@ -165,16 +164,9 @@ module ApplianceConsole
165
164
  AwesomeSpawn.run!("/sbin/restorecon -R -v #{mount_point}")
166
165
  end
167
166
 
168
- def with_pg_connection
169
- conn = PG.connect(:user => "postgres", :dbname => "postgres")
170
- yield conn
171
- ensure
172
- conn.close
173
- end
174
-
175
167
  def apply_initial_configuration
176
168
  shared_buffers = run_as_evm_server ? SHARED_DB_SHARED_BUFFERS : DEDICATED_DB_SHARED_BUFFERS
177
- with_pg_connection { |conn| conn.exec("ALTER SYSTEM SET shared_buffers TO #{shared_buffers}") }
169
+ PostgresAdmin.with_pg_connection { |conn| conn.exec("ALTER SYSTEM SET shared_buffers TO #{shared_buffers}") }
178
170
 
179
171
  restart_postgres
180
172
  end
@@ -4,6 +4,8 @@ require 'net/scp'
4
4
  require 'active_support/all'
5
5
  require 'manageiq-password'
6
6
 
7
+ require_relative './manageiq_user_mixin'
8
+
7
9
  module ManageIQ
8
10
  module ApplianceConsole
9
11
  CERT_DIR = ENV['KEY_ROOT'] || ManageIQ::ApplianceConsole::RAILS_ROOT.join("certs")
@@ -11,6 +13,8 @@ module ApplianceConsole
11
13
  NEW_KEY_FILE = "#{KEY_FILE}.tmp".freeze
12
14
 
13
15
  class KeyConfiguration
16
+ include ManageIQ::ApplianceConsole::ManageiqUserMixin
17
+
14
18
  attr_accessor :host, :login, :password, :key_path, :action, :force
15
19
 
16
20
  def initialize(options = {})
@@ -89,7 +93,9 @@ module ApplianceConsole
89
93
  end
90
94
 
91
95
  def create_key
92
- ManageIQ::Password.generate_symmetric(NEW_KEY_FILE) && true
96
+ return unless !!ManageIQ::Password.generate_symmetric(NEW_KEY_FILE)
97
+
98
+ File.chown(manageiq_uid, manageiq_gid, NEW_KEY_FILE)
93
99
  end
94
100
 
95
101
  def fetch_key
@@ -97,6 +103,7 @@ module ApplianceConsole
97
103
  Net::SCP.start(host, login, :password => password) do |scp|
98
104
  scp.download!(key_path, NEW_KEY_FILE)
99
105
  end
106
+ File.chown(manageiq_uid, manageiq_gid, NEW_KEY_FILE)
100
107
  File.exist?(NEW_KEY_FILE)
101
108
  rescue => e
102
109
  say("Failed to fetch key: #{e.message}")
@@ -0,0 +1,15 @@
1
+ module ManageIQ
2
+ module ApplianceConsole
3
+ module ManageiqUserMixin
4
+ extend ActiveSupport::Concern
5
+
6
+ def manageiq_uid
7
+ @manageiq_uid ||= Process::UID.from_name("manageiq")
8
+ end
9
+
10
+ def manageiq_gid
11
+ @manageiq_gid ||= Process::GID.from_name("manageiq")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,9 +1,13 @@
1
1
  require 'active_support/core_ext/module/delegation'
2
2
  require 'pathname'
3
3
 
4
+ require_relative './manageiq_user_mixin'
5
+
4
6
  module ManageIQ
5
7
  module ApplianceConsole
6
8
  class MessageConfiguration
9
+ include ManageIQ::ApplianceConsole::ManageiqUserMixin
10
+
7
11
  attr_reader :message_keystore_username, :message_keystore_password,
8
12
  :message_server_host, :message_server_port,
9
13
  :miq_config_dir_path, :config_dir_path, :sample_config_dir_path,
@@ -116,7 +120,10 @@ module ManageIQ
116
120
  messaging_yaml["production"]["security.protocol"] = "PLAINTEXT"
117
121
  end
118
122
 
119
- File.write(messaging_yaml_path, messaging_yaml.to_yaml)
123
+ File.open(messaging_yaml_path, "w") do |f|
124
+ f.write(messaging_yaml.to_yaml)
125
+ f.chown(manageiq_uid, manageiq_gid)
126
+ end
120
127
  end
121
128
 
122
129
  def remove_installed_files
@@ -175,8 +182,7 @@ module ManageIQ
175
182
  def configure_messaging_type(value)
176
183
  say(__method__.to_s.tr("_", " ").titleize)
177
184
 
178
- result = ManageIQ::ApplianceConsole::Utilities.rake_run("evm:settings:set", ["/prototype/messaging_type=#{value}"])
179
- raise parse_errors(result).join(', ') if result.failure?
185
+ ManageIQ::ApplianceConsole::Utilities.rake_run!("evm:settings:set", ["/prototype/messaging_type=#{value}"])
180
186
  end
181
187
 
182
188
  def restart_evmserverd
@@ -82,6 +82,8 @@ module ManageIQ
82
82
  def fetch_from_server(src_file, dst_file)
83
83
  return if file_found?(dst_file)
84
84
 
85
+ FileUtils.mkdir_p(dst_file.dirname) unless dst_file.dirname.directory?
86
+
85
87
  Net::SCP.start(message_server_host, message_server_username, :password => message_server_password) do |scp|
86
88
  scp.download!(src_file, dst_file)
87
89
  end
@@ -184,6 +184,8 @@ module ManageIQ
184
184
 
185
185
  keystore_params = assemble_keystore_params
186
186
 
187
+ FileUtils.mkdir_p(keystore_dir_path) unless keystore_dir_path.directory?
188
+
187
189
  # Generte a Java keystore and key pair, creating keystore.jks
188
190
  # :stdin_data provides the -storepass twice to confirm and an extra CR to accept the same password for -keypass
189
191
  AwesomeSpawn.run!("keytool", :params => keystore_params, :stdin_data => "#{message_keystore_password}\n#{message_keystore_password}\n\n")
@@ -1,6 +1,7 @@
1
1
  require 'awesome_spawn'
2
2
  require 'pathname'
3
3
  require 'linux_admin'
4
+ require 'pg'
4
5
 
5
6
  module ManageIQ
6
7
  module ApplianceConsole
@@ -46,6 +47,13 @@ module ApplianceConsole
46
47
  "xfs".freeze
47
48
  end
48
49
 
50
+ def self.with_pg_connection(db_opts = {:user => user, :dbname => user})
51
+ conn = PG.connect(db_opts)
52
+ yield conn
53
+ ensure
54
+ conn.close if conn
55
+ end
56
+
49
57
  def self.initialized?
50
58
  !Dir[data_directory.join("*")].empty?
51
59
  end
@@ -100,13 +108,13 @@ module ApplianceConsole
100
108
 
101
109
  def self.restore(opts)
102
110
  file = opts[:local_file]
103
- backup_type = opts.delete(:backup_type)
111
+ backup_type = opts.delete(:backup_type) || validate_backup_file_type(file)
112
+
113
+ prepare_restore(backup_type, opts[:dbname])
104
114
 
105
- case
106
- when backup_type == :pgdump then restore_pg_dump(opts)
107
- when backup_type == :basebackup then restore_pg_basebackup(file)
108
- when pg_dump_file?(file) then restore_pg_dump(opts)
109
- when base_backup_file?(file) then restore_pg_basebackup(file)
115
+ case backup_type
116
+ when :pgdump then restore_pg_dump(opts)
117
+ when :basebackup then restore_pg_basebackup(file)
110
118
  else
111
119
  raise "#{file} is not a database backup"
112
120
  end
@@ -142,6 +150,7 @@ module ApplianceConsole
142
150
  args = combine_command_args(opts, :format => "c", :file => opts[:local_file], nil => dbname)
143
151
  args = handle_multi_value_pg_dump_args!(opts, args)
144
152
 
153
+ FileUtils.mkdir_p(File.dirname(opts.fetch(:local_file, "")))
145
154
  run_command_with_logging("pg_dump", opts, args)
146
155
  opts[:local_file]
147
156
  end
@@ -320,6 +329,84 @@ module ApplianceConsole
320
329
  ensure
321
330
  File.delete(error_path) if File.exist?(error_path)
322
331
  end
332
+
333
+ private_class_method def self.prepare_restore(backup_type, dbname)
334
+ if application_connections?
335
+ message = "Database restore failed. Shut down all evmserverd processes before attempting a database restore"
336
+ ManageIQ::ApplianceConsole.logger.error(message)
337
+ raise message
338
+ end
339
+
340
+ disable_replication(dbname)
341
+
342
+ conn_count = connection_count(backup_type, dbname)
343
+ if conn_count > 1
344
+ message = "Database restore failed. #{conn_count - 1} connections remain to the database."
345
+ ManageIQ::ApplianceConsole.logger.error(message)
346
+ raise message
347
+ end
348
+ end
349
+
350
+ private_class_method def self.application_connections?
351
+ result = [{"count" => 0}]
352
+
353
+ with_pg_connection do |conn|
354
+ result = conn.exec("SELECT COUNT(pid) FROM pg_stat_activity WHERE application_name LIKE '%MIQ%'")
355
+ end
356
+
357
+ result[0]["count"].to_i > 0
358
+ end
359
+
360
+ private_class_method def self.disable_replication(dbname)
361
+ require 'pg/logical_replication'
362
+
363
+ with_pg_connection do |conn|
364
+ pglogical = PG::LogicalReplication::Client.new(conn)
365
+
366
+ if pglogical.subscriber?
367
+ pglogical.subcriptions(dbname).each do |subscriber|
368
+ sub_id = subscriber["subscription_name"]
369
+ begin
370
+ pglogical.drop_subscription(sub_id, true)
371
+ rescue PG::InternalError => e
372
+ raise unless e.message.include?("could not connect to publisher")
373
+ raise unless e.message.match?(/replication slot .* does not exist/)
374
+
375
+ pglogical.disable_subscription(sub_id).check
376
+ pglogical.alter_subscription_options(sub_id, "slot_name" => "NONE")
377
+ pglogical.drop_subscription(sub_id, true)
378
+ end
379
+ end
380
+ elsif pglogical.publishes?('miq')
381
+ pglogical.drop_publication('miq')
382
+ end
383
+ end
384
+ end
385
+
386
+ private_class_method def self.connection_count(backup_type, dbname)
387
+ result = nil
388
+
389
+ with_pg_connection do |conn|
390
+ query = "SELECT COUNT(pid) FROM pg_stat_activity"
391
+ query << " WHERE backend_type = 'client backend'" if backup_type == :basebackup
392
+ query << " WHERE datname = '#{dbname}'" if backup_type == :pgdump
393
+ result = conn.exec(query)
394
+ end
395
+
396
+ result[0]["count"].to_i
397
+ end
398
+
399
+ private_class_method def self.validate_backup_file_type(file)
400
+ if base_backup_file?(file)
401
+ :basebackup
402
+ elsif pg_dump_file?(file)
403
+ :pgdump
404
+ else
405
+ message = "#{filename} is not in a recognized database backup format"
406
+ ManageIQ::ApplianceConsole.error(message)
407
+ raise message
408
+ end
409
+ end
323
410
  end
324
411
  end
325
412
  end
@@ -16,6 +16,16 @@ module ApplianceConsole
16
16
  result
17
17
  end
18
18
 
19
+ def self.rake_run!(task, params)
20
+ result = rake_run(task, params)
21
+ if result.failure?
22
+ parsed_errors = result.error.split("\n").select { |line| line.match?(/^error: /i) }.join(', ')
23
+ raise parsed_errors
24
+ end
25
+
26
+ result
27
+ end
28
+
19
29
  def self.db_connections
20
30
  result = AwesomeSpawn.run("bin/rails runner",
21
31
  :params => ["exit EvmDatabaseOps.database_connections"],
@@ -1,5 +1,5 @@
1
1
  module ManageIQ
2
2
  module ApplianceConsole
3
- VERSION = '6.1.0'.freeze
3
+ VERSION = '7.0.0'.freeze
4
4
  end
5
5
  end
@@ -39,22 +39,6 @@ en:
39
39
  summary: Summary Information
40
40
  quit: Quit
41
41
  database_admin:
42
- menu_order:
43
- - local
44
- - nfs
45
- - smb
46
- - s3
47
- - ftp
48
- - swift
49
42
  local: Local file
50
- nfs: Network File System (NFS)
51
- smb: Samba (SMB)
52
- s3: Amazon S3 (S3)
53
- ftp: File Transfer Protocol (FTP)
54
- swift: OpenStack Swift (Swift)
55
43
  sample_url:
56
44
  nfs: nfs://host.mydomain.com/exported/my_exported_folder/db.backup
57
- smb: smb://host.mydomain.com/my_share/daily_backup/db.backup
58
- s3: s3://mybucket/my_subdirectory/daily_backup/db.backup
59
- ftp: ftp://host.mydomain.com/path/to/daily_backup/db.backup
60
- swift: swift://host.mydomain.com/path/to/daily_backup/db.backup
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_runtime_dependency "net-scp", "~> 1.2.1"
33
33
  spec.add_runtime_dependency "optimist", "~> 3.0"
34
34
  spec.add_runtime_dependency "pg"
35
+ spec.add_runtime_dependency "pg-logical_replication"
35
36
  spec.add_runtime_dependency "rbnacl", ">= 3.2", "< 5.0"
36
37
 
37
38
  spec.add_development_dependency "bundler"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: manageiq-appliance_console
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ManageIQ Developers
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-30 00:00:00.000000000 Z
11
+ date: 2021-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -184,6 +184,20 @@ dependencies:
184
184
  - - ">="
185
185
  - !ruby/object:Gem::Version
186
186
  version: '0'
187
+ - !ruby/object:Gem::Dependency
188
+ name: pg-logical_replication
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ type: :runtime
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
187
201
  - !ruby/object:Gem::Dependency
188
202
  name: rbnacl
189
203
  requirement: !ruby/object:Gem::Requirement
@@ -303,7 +317,7 @@ dependencies:
303
317
  - !ruby/object:Gem::Version
304
318
  version: '0'
305
319
  description: ManageIQ Appliance Console
306
- email:
320
+ email:
307
321
  executables:
308
322
  - appliance_console
309
323
  - appliance_console_cli
@@ -347,6 +361,7 @@ files:
347
361
  - lib/manageiq/appliance_console/logger.rb
348
362
  - lib/manageiq/appliance_console/logging.rb
349
363
  - lib/manageiq/appliance_console/logical_volume_management.rb
364
+ - lib/manageiq/appliance_console/manageiq_user_mixin.rb
350
365
  - lib/manageiq/appliance_console/message_configuration.rb
351
366
  - lib/manageiq/appliance_console/message_configuration_client.rb
352
367
  - lib/manageiq/appliance_console/message_configuration_server.rb
@@ -367,7 +382,7 @@ homepage: https://github.com/ManageIQ/manageiq-appliance_console
367
382
  licenses:
368
383
  - Apache-2.0
369
384
  metadata: {}
370
- post_install_message:
385
+ post_install_message:
371
386
  rdoc_options: []
372
387
  require_paths:
373
388
  - lib
@@ -382,8 +397,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
382
397
  - !ruby/object:Gem::Version
383
398
  version: '0'
384
399
  requirements: []
385
- rubygems_version: 3.0.6
386
- signing_key:
400
+ rubygems_version: 3.2.17
401
+ signing_key:
387
402
  specification_version: 4
388
403
  summary: ManageIQ Appliance Console
389
404
  test_files: []