Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Openstack Swift DB Backups #17967

Merged
merged 11 commits into from
Oct 11, 2018
7 changes: 4 additions & 3 deletions app/models/database_backup.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
class DatabaseBackup < ApplicationRecord
SUPPORTED_DEPOTS = {
'smb' => 'Samba',
'nfs' => 'Network File System',
's3' => 'AWS S3'
'smb' => 'Samba',
'nfs' => 'Network File System',
's3' => 'AWS S3',
'swift' => 'OpenStack Swift'
}.freeze

def self.supported_depots
Expand Down
4 changes: 4 additions & 0 deletions app/models/file_depot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ def requires_support_case?
def upload_file(file)
@file = file
end

def merged_uri
uri
end
end
67 changes: 67 additions & 0 deletions app/models/file_depot_swift.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
class FileDepotSwift < FileDepot
def self.uri_prefix
"swift"
end

def self.validate_settings(settings)
new(:uri => settings[:uri]).verify_credentials(nil, settings.slice(:username, :password))
end

def connect(options = {})
openstack_handle(options).connect(options)
end

def openstack_handle(options = {})
require 'manageiq/providers/openstack/legacy/openstack_handle'
@openstack_handle ||= begin
username = options[:username] || authentication_userid(options[:auth_type])
password = options[:password] || authentication_password(options[:auth_type])
uri = options[:uri]
address = URI(uri).host
port = URI(uri).port

extra_options = {
:ssl_ca_file => ::Settings.ssl.ssl_ca_file,
:ssl_ca_path => ::Settings.ssl.ssl_ca_path,
:ssl_cert_store => OpenSSL::X509::Store.new
}
extra_options[:domain_id] = v3_domain_ident
extra_options[:service] = "Compute"
extra_options[:omit_default_port] = ::Settings.ems.ems_openstack.excon.omit_default_port
extra_options[:read_timeout] = ::Settings.ems.ems_openstack.excon.read_timeout
begin
OpenstackHandle::Handle.new(username, password, address, port, keystone_api_version, security_protocol, extra_options)
rescue => err
msg = "Error connecting to Swift host #{address}. #{err}"
jerryk55 marked this conversation as resolved.
Show resolved Hide resolved
logger.error(msg)
raise err, msg, err.backtrace
end
end
end

def verify_credentials(auth_type = 'default', options = {})
NickLaMuro marked this conversation as resolved.
Show resolved Hide resolved
host = URI(options[:uri]).host
options[:auth_type] = auth_type
connect(options.merge(:auth_type => auth_type))
rescue Excon::Errors::Unauthorized => err
msg = "Access to Swift host #{host} failed due to a bad username or password."
logger.error("#{msg} #{err}")
raise msg
rescue => err
msg = "Error connecting to Swift host #{host}. #{err}"
carbonin marked this conversation as resolved.
Show resolved Hide resolved
logger.error(msg)
raise err, msg, err.backtrace
end

def merged_uri(uri, api_port)
uri = URI(uri)
uri.port = api_port.presence || 5000
query_elements = []
query_elements << "region=#{openstack_region}" if openstack_region.present?
query_elements << "api_version=#{keystone_api_version}" if keystone_api_version.present?
query_elements << "domain_id=#{v3_domain_ident}" if v3_domain_ident.present?
query_elements << "security_protocol=#{security_protocol}" if security_protocol.present?
uri.query = query_elements.join('&').presence
uri.to_s
end
end
20 changes: 13 additions & 7 deletions app/models/miq_schedule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -331,16 +331,22 @@ def validate_file_depot # TODO: Do we need this if the validations are on the F
end

def verify_file_depot(params) # TODO: This logic belongs in the UI, not sure where
depot_class = FileDepot.supported_protocols[params[:uri_prefix]]
depot = file_depot.class.name == depot_class ? file_depot : build_file_depot(:type => depot_class)
depot.name = params[:name]
depot.uri = params[:uri]
depot.aws_region = params[:aws_region]
depot_class = FileDepot.supported_protocols[params[:uri_prefix]]
depot = file_depot.class.name == depot_class ? file_depot : build_file_depot(:type => depot_class)
depot.name = params[:name]
uri = params[:uri]
api_port = params[:swift_api_port]
depot.aws_region = params[:aws_region]
depot.openstack_region = params[:openstack_region]
depot.keystone_api_version = params[:keystone_api_version]
depot.v3_domain_ident = params[:v3_domain_ident]
depot.security_protocol = params[:security_protocol]
depot.uri = api_port.blank? ? uri : depot.merged_uri(URI(uri), api_port)
if params[:save]
file_depot.save!
file_depot.update_authentication(:default => {:userid => params[:username], :password => params[:password]}) if (params[:username] || params[:password]) && depot.class.requires_credentials?
else
depot.verify_credentials(nil, params.slice(:username, :password)) if depot.class.requires_credentials?
elsif depot.class.requires_credentials?
depot.verify_credentials(nil, params)
carbonin marked this conversation as resolved.
Show resolved Hide resolved
end
end

Expand Down
7 changes: 4 additions & 3 deletions app/models/mixins/file_depot_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
module FileDepotMixin
extend ActiveSupport::Concern
SUPPORTED_DEPOTS = {
'smb' => 'Samba',
'nfs' => 'Network File System',
's3' => 'Amazon Web Services'
'smb' => 'Samba',
'nfs' => 'Network File System',
's3' => 'Amazon Web Services',
'swift' => 'OpenStack Swift'
}.freeze

included do
Expand Down
7 changes: 6 additions & 1 deletion lib/evm_database_ops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ def self.restore(db_opts, connect_opts = {})
else
connect_opts[:remote_file_name] ||= File.basename(backup_file_name(action))
backup_folder = action == :dump ? "db_dump" : "db_backup"
uri = File.join(connect_opts[:uri], backup_folder, connect_opts[:remote_file_name])
#
# If the passed in URI contains query parameters, ignore them
# when creating the dump file name. They'll be used in the session object.
#
connect_opts_uri = connect_opts[:uri].split('?')[0]
uri = File.join(connect_opts_uri, backup_folder, connect_opts[:remote_file_name])
NickLaMuro marked this conversation as resolved.
Show resolved Hide resolved
end
else
uri = db_opts[:local_file]
Expand Down
1 change: 1 addition & 0 deletions locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ en:
FileDepotFtpAnonymous: Anonymous FTP
FileDepotNfs: NFS
FileDepotS3: AWS S3
FileDepotSwift: OpenStack Swift
FileDepotSmb: Samba
Filesystem: File
Flavor: Flavor
Expand Down
1 change: 1 addition & 0 deletions spec/models/file_depot_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
expect(described_class.depot_description_to_class("NFS")).to eq(FileDepotNfs)
expect(described_class.depot_description_to_class("Samba")).to eq(FileDepotSmb)
expect(described_class.depot_description_to_class("Anonymous FTP")).to eq(FileDepotFtpAnonymous)
expect(described_class.depot_description_to_class("OpenStack Swift")).to eq(FileDepotSwift)
end
end
29 changes: 29 additions & 0 deletions spec/models/file_depot_swift_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
describe FileDepotSwift do
let(:uri) { "swift://server.example.com/bucket" }
let(:merged_uri) { "swift://server.example.com:5678/bucket?region=test_openstack_region&api_version=v3&domain_id=default" }
let(:merged_default_uri) { "swift://server.example.com:5000/bucket?region=test_openstack_region&api_version=v3&domain_id=default" }
let(:file_depot_swift) { FileDepotSwift.new(:uri => uri) }
it "should require credentials" do
expect(FileDepotSwift.requires_credentials?).to eq true
end

it "should return a valid prefix" do
expect(FileDepotSwift.uri_prefix).to eq "swift"
end

describe "#merged_uri" do
before do
file_depot_swift.openstack_region = "test_openstack_region"
file_depot_swift.keystone_api_version = "v3"
file_depot_swift.v3_domain_ident = "default"
end

it "should return a merged uri with query strings given an empty port" do
expect(file_depot_swift.merged_uri(uri, nil)).to eq merged_default_uri
end

it "should return a merged uri with query strings when given a valid port" do
expect(file_depot_swift.merged_uri(uri, "5678")).to eq merged_uri
end
end
end