diff --git a/app/models/fileset.rb b/app/models/fileset.rb index 446d7c1..e840ca5 100644 --- a/app/models/fileset.rb +++ b/app/models/fileset.rb @@ -1,90 +1,88 @@ class Fileset < ActiveRecord::Base - establish_connection Baas::settings[:local_db] - serialize :exclude_directions serialize :include_directions, JSON attr_accessor :include_files belongs_to :host has_many :job_templates validates :name, presence: true, uniqueness: { scope: :host } validate :has_included_files, on: :create validates_with NameValidator before_save :sanitize_exclude_directions, :sanitize_include_directions DEFAULT_EXCLUDED = %w{/var/lib/bacula /proc /tmp /.journal /.fsck /bacula} DEFAULT_INCLUDE_OPTIONS = { signature: :SHA1, compression: :GZIP } DEFAULT_INCLUDE_FILE_LIST = ['/'] def to_bacula_config_array ['FileSet {'] + [" Name = \"#{name_for_config}\""] + include_directions_to_config_array + exclude_directions_to_config_array + ['}'] end def name_for_config [host.name, name].join(' ') end private def has_included_files if include_files.blank? || include_files.all?(&:blank?) errors.add(:include_files, :cant_be_blank) end end def sanitize_include_directions files = include_files.compact.uniq.keep_if(&:present?) return false if files.blank? self.include_directions = { options: DEFAULT_INCLUDE_OPTIONS, file: files } end def sanitize_exclude_directions self.exclude_directions = exclude_directions.keep_if(&:present?).uniq rescue nil end def exclude_directions_to_config_array return [] if exclude_directions.empty? [' Exclude {'] + exclude_directions.map { |x| " File = \"#{x}\"" } + [' }'] end def include_directions_to_config_array return [] if include_directions.blank? [" Include {"] + included_options + included_files + [' }'] end def included_options formatted = [" Options {"] options = include_directions.deep_symbolize_keys[:options]. reverse_merge(DEFAULT_INCLUDE_OPTIONS) options.each do |k,v| if not [:wildfile].include? k formatted << " #{k} = #{v}" else formatted << v.map { |f| " #{k} = \"#{f}\"" } end end formatted << " }" formatted end def included_files include_directions['file'].map { |f| " File = #{f}" } end def included_wildfile include_directions['wildfile'].map { |f| " wildfile = \"#{f}\"" }.join("\n") end end diff --git a/app/models/host.rb b/app/models/host.rb index 250e736..f3f5f6e 100644 --- a/app/models/host.rb +++ b/app/models/host.rb @@ -1,262 +1,260 @@ # The bacula database must be independent from all of our application logic. # For this reason we have Host which is the application equivalent of a Bacula Client. # # A host is being created from our application. When it receives all the configuration # which is required it gets dispatched to bacula through some configuration files. After # that, a client with the exact same config is generated by bacula. class Host < ActiveRecord::Base - establish_connection Baas::settings[:local_db] - STATUSES = { pending: 0, configured: 1, dispatched: 2, deployed: 3, updated: 4, redispatched: 5, for_removal: 6 } has_many :ownerships has_many :users, through: :ownerships, inverse_of: :hosts belongs_to :client, class_name: :Client, foreign_key: :name, primary_key: :name belongs_to :verifier, class_name: :User, foreign_key: :verifier_id, primary_key: :id has_many :filesets, dependent: :destroy has_many :job_templates, dependent: :destroy has_many :schedules, dependent: :destroy validates :file_retention, :job_retention, :port, :password, presence: true validates :port, numericality: true validates :fqdn, presence: true, uniqueness: true validate :fqdn_format scope :not_baculized, -> { joins("left join Client on Client.Name = hosts.name").where(Client: { Name: nil }) } scope :unverified, -> { where(verified: false) } before_validation :set_retention, :unset_baculized, :sanitize_name state_machine :status, initial: :pending do STATUSES.each do |status_name, value| state status_name, value: value end after_transition [:dispatched, :redispatched, :configured, :updated] => :deployed do |host| host.job_templates.enabled. update_all(baculized: true, baculized_at: Time.now, updated_at: Time.now) end event :add_configuration do transition [:pending, :dispatched] => :configured end event :dispatch do transition :configured => :dispatched end event :redispatch do transition :updated => :redispatched end event :set_deployed do transition [:dispatched, :redispatched, :configured, :updated] => :deployed end event :change_deployed_config do transition [:deployed, :redispatched, :for_removal] => :updated end event :mark_for_removal do transition [:dispatched, :deployed, :updated, :redispatched] => :for_removal end event :disable do transition all => :pending end end # Constructs the final Bacula configuration for the host by appending configs for # # * Client # * Jobs # * Schedules # * Filesets # # by calling their `to_bacula_config_array` methods. # # @return [Array] containing each element's configuration line by line def baculize_config templates = job_templates.enabled.includes(:fileset, :schedule) result = [self] + templates.map {|x| [x, x.fileset, x.schedule] }.flatten.compact.uniq result.map(&:to_bacula_config_array) end # Constructs an array where each element is a line for the Client's bacula config # # @return [Array] def to_bacula_config_array [ "Client {", " Name = #{name}", " Address = #{fqdn}", " FDPort = #{port}", " Catalog = #{client_settings[:catalog]}", " Password = \"#{password}\"", " File Retention = #{file_retention} #{file_retention_period_type}", " Job Retention = #{job_retention} #{job_retention_period_type}", " AutoPrune = #{auto_prune_human}", "}" ] end # Shows the host's auto_prune setting def auto_prune_human client_settings[:autoprune] end # Uploads the host's config to bacula # Reloads bacula server # # It updates the host's status accordingly def dispatch_to_bacula return false if not needs_dispatch? bacula_handler.deploy_config end # Removes a Host from bacula configuration. # Reloads bacula server # # If all go well it changes the host's status and returns true def remove_from_bacula return false unless needs_revoke? bacula_handler.undeploy_config end # Restores a host's backup to a preselected location # # @param location[String] the desired restore location def restore(location) return false if not restorable? bacula_handler.restore(location) end # Runs the given backup job ASAP def backup_now(job_name) bacula_handler.backup_now(job_name) end # Determinex weather a host: # # * has all it takes to be deployed but # * the config is not yet sent to bacula # # @return [Boolean] def needs_dispatch? verified? && (can_dispatch? || can_redispatch?) end # Determines weather a host is marked for removal # # @return [Boolean] def needs_revoke? for_removal? end # Handles the host's job changes by updating the host's status def recalculate if job_templates(true).enabled.any? add_configuration || change_deployed_config else mark_for_removal || disable end end # Fetches an info message concerning the host's deploy status def display_message if !verified? { message: 'Your host needs to be verified by an admin', severity: :alert } elsif pending? { message: 'client not configured yet', severity: :alert } elsif configured? || dispatched? { message: 'client not deployed to Bacula', severity: :alert } elsif updated? || redispatched? { message: 'client configuration changed, deploy needed', severity: :alert } elsif for_removal? { message: 'pending client configuration withdraw', severity: :error } elsif inactive? { message: 'client disabled', severity: :alert } end end # Determines if a host can issue a restore job. # # @returns [Boolean] true if the host's client can issue a restore job def restorable? client.present? && client.is_backed_up? end # @return [User] the first of the host's users def first_user users.order('ownerships.created_at asc').first end # Marks the host as verified and sets the relevant metadata # # @param admin_verifier[Integer] the verifier's id def verify(admin_verifier) self.verified = true self.verifier_id = admin_verifier self.verified_at = Time.now save end private # automatic setters def sanitize_name self.name = fqdn end # Sets the file and job retention according to the global settings def set_retention self.file_retention = client_settings[:file_retention] self.file_retention_period_type = client_settings[:file_retention_period_type] self.job_retention = client_settings[:job_retention] self.job_retention_period_type = client_settings[:job_retention_period_type] end def unset_baculized self.baculized = false if new_record? true end # validation def fqdn_format regex = /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(? { where(enabled: true) } # Constructs an array where each element is a line for the Job's bacula config # # @return [Array] def to_bacula_config_array ['Job {'] + options_array.map { |x| " #{x}" } + job_settings.map { |k,v| " #{k.capitalize} = #{v}" } + ['}'] end # Fetches the Job's priority def priority job_settings[:priority] end # Helper method for the job template's enabled status def enabled_human enabled? ? 'yes' : 'no' end # Helper method for the job template's schedule name # # @return [String] The schedule's name or nothing def schedule_human schedule.present? ? schedule.name : '-' end # Generates a name that will be used for the configuration file. # It is the name that will be sent to Bacula through the configuration # files. # # @return [String] def name_for_config "#{host.name} #{name}" end # Sends a hot backup request to Bacula via BaculaHandler def backup_now return false if not (enabled? && baculized? && backup?) host.backup_now(name) end private def name_format unless name =~ /^[a-zA-Z0-1\.-_ ]+$/ self.errors.add(:name, :format) end end def notify_host host.recalculate end # Sets the default job_type as backup def set_job_type self.job_type = :backup if job_type.nil? end def options_array result = [ "Name = \"#{name_for_config}\"", "FileSet = \"#{fileset.name_for_config}\"", "Client = \"#{host.name}\"", "Type = \"#{job_type.capitalize}\"", "Schedule = \"#{schedule.name_for_config}\"" ] if client_before_run_file.present? result += ["Client Run Before Job = \"#{client_before_run_file}\""] end if client_after_run_file.present? result += ["Client Run After Job = \"#{client_after_run_file}\""] end result end # Fetches and memoizes the general configuration settings for Jobs # # @see ConfigurationSetting.current_job_settings # @return [Hash] containing the settings def job_settings @job_settings ||= ConfigurationSetting.current_job_settings end end diff --git a/app/models/ownership.rb b/app/models/ownership.rb index a6c25fe..3962147 100644 --- a/app/models/ownership.rb +++ b/app/models/ownership.rb @@ -1,6 +1,4 @@ class Ownership < ActiveRecord::Base - establish_connection Baas::settings[:local_db] - belongs_to :user belongs_to :host end diff --git a/app/models/user.rb b/app/models/user.rb index 6ac0b34..224d912 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,25 +1,23 @@ class User < ActiveRecord::Base - establish_connection Baas::settings[:local_db] - has_many :ownerships has_many :hosts, through: :ownerships, inverse_of: :users enum user_type: { institutional: 0, vima: 1, okeanos: 2, admin: 3 } validates :username, :user_type, presence: true # Composes the user's display name from the user's username and email # # @return [String] def display_name "#{username} <#{email}>" end # Determines if the user must select hosts from a list or enter their # FQDN manually # # @return [Boolean] def needs_host_list? vima? || okeanos? end end diff --git a/config/initializers/00_settings.rb b/config/initializers/00_settings.rb index f158398..9b8071c 100644 --- a/config/initializers/00_settings.rb +++ b/config/initializers/00_settings.rb @@ -1,3 +1 @@ -Baas.settings local_db: "local_#{Rails.env}".to_sym - Archiving.settings vima_oauth_enabled: true diff --git a/db/migrate/20151020192733_create_users_table.rb b/db/migrate/20151020192733_create_users_table.rb index e371269..76c0470 100644 --- a/db/migrate/20151020192733_create_users_table.rb +++ b/db/migrate/20151020192733_create_users_table.rb @@ -1,15 +1,11 @@ class CreateUsersTable < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change create_table :users do |t| t.string :username, null: false t.string :email t.integer :user_type, limit: 1, null: false t.boolean :enabled, default: false t.timestamps end end end diff --git a/db/migrate/20151024162823_create_hosts_table.rb b/db/migrate/20151024162823_create_hosts_table.rb index 6e587e1..067ab36 100644 --- a/db/migrate/20151024162823_create_hosts_table.rb +++ b/db/migrate/20151024162823_create_hosts_table.rb @@ -1,19 +1,15 @@ class CreateHostsTable < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change create_table :hosts do |t| t.binary :name, limit: 255, null: false t.binary :fqdn, limit: 255, null: false t.integer :port, null: false t.integer :file_retention, null: false t.integer :job_retention, null: false t.timestamps end add_index :hosts, :name, unique: true, length: { name: 128}, using: :btree end end diff --git a/db/migrate/20151025070113_add_password_to_hosts.rb b/db/migrate/20151025070113_add_password_to_hosts.rb index d05c7b0..5cd4167 100644 --- a/db/migrate/20151025070113_add_password_to_hosts.rb +++ b/db/migrate/20151025070113_add_password_to_hosts.rb @@ -1,9 +1,5 @@ class AddPasswordToHosts < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :hosts, :password, :string end end diff --git a/db/migrate/20151025110412_add_baculized_field_to_hosts.rb b/db/migrate/20151025110412_add_baculized_field_to_hosts.rb index 9c36227..d37a08a 100644 --- a/db/migrate/20151025110412_add_baculized_field_to_hosts.rb +++ b/db/migrate/20151025110412_add_baculized_field_to_hosts.rb @@ -1,10 +1,6 @@ class AddBaculizedFieldToHosts < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :hosts, :baculized, :boolean, default: false, null: false add_column :hosts, :baculized_at, :datetime end end diff --git a/db/migrate/20151025142916_add_status_and_client_id_to_hosts.rb b/db/migrate/20151025142916_add_status_and_client_id_to_hosts.rb index f22070b..e96deb1 100644 --- a/db/migrate/20151025142916_add_status_and_client_id_to_hosts.rb +++ b/db/migrate/20151025142916_add_status_and_client_id_to_hosts.rb @@ -1,10 +1,6 @@ class AddStatusAndClientIdToHosts < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :hosts, :status, :integer, limit: 1, default: 0 add_column :hosts, :client_id, :integer end end diff --git a/db/migrate/20151026180147_create_fileset_table.rb b/db/migrate/20151026180147_create_fileset_table.rb index a8aa265..ae43d3d 100644 --- a/db/migrate/20151026180147_create_fileset_table.rb +++ b/db/migrate/20151026180147_create_fileset_table.rb @@ -1,22 +1,18 @@ class CreateFilesetTable < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up create_table :filesets do |t| t.string :name t.integer :host_id t.text :exclude_directions t.text :include_directions t.timestamps end add_index :filesets, :host_id end def down drop_table :filesets end end diff --git a/db/migrate/20151028083132_create_schedules.rb b/db/migrate/20151028083132_create_schedules.rb index be18d43..dd78388 100644 --- a/db/migrate/20151028083132_create_schedules.rb +++ b/db/migrate/20151028083132_create_schedules.rb @@ -1,12 +1,8 @@ class CreateSchedules < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change create_table :schedules do |t| t.string :name t.string :runs end end end diff --git a/db/migrate/20151028171930_create_job_templates.rb b/db/migrate/20151028171930_create_job_templates.rb index d395d47..1f6fc0e 100644 --- a/db/migrate/20151028171930_create_job_templates.rb +++ b/db/migrate/20151028171930_create_job_templates.rb @@ -1,17 +1,13 @@ class CreateJobTemplates < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change create_table :job_templates do |t| t.string :name, null: false t.integer :job_type, limit: 1 t.integer :host_id t.integer :fileset_id t.integer :schedule_id t.timestamps end end end diff --git a/db/migrate/20151029095243_add_enabled_to_job_templates.rb b/db/migrate/20151029095243_add_enabled_to_job_templates.rb index 641fcc7..826a626 100644 --- a/db/migrate/20151029095243_add_enabled_to_job_templates.rb +++ b/db/migrate/20151029095243_add_enabled_to_job_templates.rb @@ -1,9 +1,5 @@ class AddEnabledToJobTemplates < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :job_templates, :enabled, :boolean, default: false end end diff --git a/db/migrate/20151101142440_add_restore_location_to_job_templates.rb b/db/migrate/20151101142440_add_restore_location_to_job_templates.rb index 40eee89..2877a93 100644 --- a/db/migrate/20151101142440_add_restore_location_to_job_templates.rb +++ b/db/migrate/20151101142440_add_restore_location_to_job_templates.rb @@ -1,9 +1,5 @@ class AddRestoreLocationToJobTemplates < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :job_templates, :restore_location, :binary end end diff --git a/db/migrate/20151102194922_add_host_id_to_schedules.rb b/db/migrate/20151102194922_add_host_id_to_schedules.rb index 732a038..98e8f49 100644 --- a/db/migrate/20151102194922_add_host_id_to_schedules.rb +++ b/db/migrate/20151102194922_add_host_id_to_schedules.rb @@ -1,11 +1,7 @@ class AddHostIdToSchedules < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :schedules, :host_id, :integer add_index :schedules, :host_id end end diff --git a/db/migrate/20151106210604_add_verified_field_to_hosts.rb b/db/migrate/20151106210604_add_verified_field_to_hosts.rb index fefdaa8..60bf97e 100644 --- a/db/migrate/20151106210604_add_verified_field_to_hosts.rb +++ b/db/migrate/20151106210604_add_verified_field_to_hosts.rb @@ -1,9 +1,5 @@ class AddVerifiedFieldToHosts < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def change add_column :hosts, :verified, :boolean, default: false end end diff --git a/db/migrate/20151112212531_create_ownerships.rb b/db/migrate/20151112212531_create_ownerships.rb index ff789ed..bbb4452 100644 --- a/db/migrate/20151112212531_create_ownerships.rb +++ b/db/migrate/20151112212531_create_ownerships.rb @@ -1,17 +1,13 @@ class CreateOwnerships < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up create_table :ownerships do |t| t.integer :user_id, index: true t.integer :host_id, index: true t.timestamps end end def down drop_table :ownerships end end diff --git a/db/migrate/20151113234535_add_baculized_to_job_templates.rb b/db/migrate/20151113234535_add_baculized_to_job_templates.rb index 25a70b3..aab1d9b 100644 --- a/db/migrate/20151113234535_add_baculized_to_job_templates.rb +++ b/db/migrate/20151113234535_add_baculized_to_job_templates.rb @@ -1,15 +1,11 @@ class AddBaculizedToJobTemplates < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up add_column :job_templates, :baculized, :boolean, default: false add_column :job_templates, :baculized_at, :datetime end def down remove_column :job_templates, :baculized remove_column :job_templates, :baculized_at end end diff --git a/db/migrate/20151120182046_add_before_after_job_fields_to_job_template.rb b/db/migrate/20151120182046_add_before_after_job_fields_to_job_template.rb index c2df99d..2519b86 100644 --- a/db/migrate/20151120182046_add_before_after_job_fields_to_job_template.rb +++ b/db/migrate/20151120182046_add_before_after_job_fields_to_job_template.rb @@ -1,15 +1,11 @@ class AddBeforeAfterJobFieldsToJobTemplate < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up add_column :job_templates, :client_before_run_file, :string add_column :job_templates, :client_after_run_file, :string end def down remove_column :job_templates, :client_before_run_file remove_column :job_templates, :client_after_run_file end end diff --git a/db/migrate/20151122124400_add_verify_details_to_hosts.rb b/db/migrate/20151122124400_add_verify_details_to_hosts.rb index 53dd3b2..3399c7d 100644 --- a/db/migrate/20151122124400_add_verify_details_to_hosts.rb +++ b/db/migrate/20151122124400_add_verify_details_to_hosts.rb @@ -1,15 +1,11 @@ class AddVerifyDetailsToHosts < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up add_column :hosts, :verified_at, :datetime add_column :hosts, :verifier_id, :integer end def down remove_column :hosts, :verified_at remove_column :hosts, :verifier_id end end diff --git a/db/migrate/20151122190659_create_settings_table.rb b/db/migrate/20151122190659_create_settings_table.rb index 2bda97d..d38f032 100644 --- a/db/migrate/20151122190659_create_settings_table.rb +++ b/db/migrate/20151122190659_create_settings_table.rb @@ -1,17 +1,13 @@ class CreateSettingsTable < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up create_table :configuration_settings do |t| t.string :job, default: {}.to_json t.string :client, default: {}.to_json t.timestamps end end def down drop_table :configuration_settings end end diff --git a/db/migrate/20151125222951_add_identifier_users.rb b/db/migrate/20151125222951_add_identifier_users.rb index 9aa10c7..36fc160 100644 --- a/db/migrate/20151125222951_add_identifier_users.rb +++ b/db/migrate/20151125222951_add_identifier_users.rb @@ -1,15 +1,11 @@ class AddIdentifierUsers < ActiveRecord::Migration - def connection - ActiveRecord::Base.establish_connection(Baas::settings[:local_db]).connection - end - def up add_column :users, :identifier, :string add_index :users, :identifier end def down remove_column :users, :identifier end end