2014-01-07 20:29:40 +00:00
|
|
|
module JamRuby
|
|
|
|
|
class IcecastServer < ActiveRecord::Base
|
|
|
|
|
|
2014-01-19 02:20:44 +00:00
|
|
|
attr_accessor :skip_config_changed_flag
|
|
|
|
|
|
2014-01-21 14:51:03 +00:00
|
|
|
attr_accessible :template_id, :mount_template_id, :limit_id, :admin_auth_id, :directory_id, :master_relay_id, :path_id, :logging_id,
|
2014-01-19 02:20:44 +00:00
|
|
|
:security_id, :config_changed, :hostname, :location, :admin_email, :fileserve, as: :admin
|
|
|
|
|
|
2014-01-21 14:51:03 +00:00
|
|
|
belongs_to :template, class_name: "JamRuby::IcecastTemplate", foreign_key: 'template_id', inverse_of: :servers
|
|
|
|
|
belongs_to :mount_template, class_name: "JamRuby::IcecastMountTemplate", foreign_key: 'mount_template_id', inverse_of: :servers
|
|
|
|
|
belongs_to :server_group, class_name: "JamRuby::IcecastServerGroup", foreign_key: 'icecast_server_group_id', inverse_of: :servers
|
2014-01-17 19:55:26 +00:00
|
|
|
|
|
|
|
|
# all are overrides, because the template defines all of these as well. When building the XML, we will prefer these if set
|
2014-01-21 14:51:03 +00:00
|
|
|
belongs_to :limit, class_name: "JamRuby::IcecastLimit", foreign_key: 'limit_id', inverse_of: :servers
|
|
|
|
|
belongs_to :admin_auth, class_name: "JamRuby::IcecastAdminAuthentication", foreign_key: 'admin_auth_id', inverse_of: :servers
|
|
|
|
|
belongs_to :directory, class_name: "JamRuby::IcecastDirectory", foreign_key: 'directory_id', inverse_of: :servers
|
|
|
|
|
belongs_to :master_relay, class_name: "JamRuby::IcecastMasterServerRelay", foreign_key: 'master_relay_id', inverse_of: :servers
|
|
|
|
|
belongs_to :path, class_name: "JamRuby::IcecastPath", foreign_key: 'path_id', inverse_of: :servers
|
|
|
|
|
belongs_to :logging, class_name: "JamRuby::IcecastLogging", foreign_key: 'logging_id', inverse_of: :servers
|
|
|
|
|
belongs_to :security, class_name: "JamRuby::IcecastSecurity", foreign_key: 'security_id', inverse_of: :servers
|
|
|
|
|
has_many :listen_socket_servers, class_name: "JamRuby::IcecastServerSocket", inverse_of: :server
|
|
|
|
|
has_many :listen_sockets, class_name: "JamRuby::IcecastListenSocket", :through => :listen_socket_servers, :source => :socket
|
2014-01-17 04:51:19 +00:00
|
|
|
|
2014-01-17 19:55:26 +00:00
|
|
|
# mounts and relays are naturally server-specific, though
|
2014-01-21 14:51:03 +00:00
|
|
|
#has_many :server_mounts, class_name: "JamRuby::IcecastServerMount", inverse_of: :server
|
|
|
|
|
has_many :mounts, class_name: "JamRuby::IcecastMount", inverse_of: :server, :foreign_key => 'icecast_server_id'
|
2014-01-19 02:20:44 +00:00
|
|
|
|
2014-01-21 14:51:03 +00:00
|
|
|
has_many :server_relays, class_name: "JamRuby::IcecastServerRelay", inverse_of: :relay
|
|
|
|
|
has_many :relays, class_name: "JamRuby::IcecastRelay", :through => :server_relays, :source => :relay
|
2014-01-17 04:51:19 +00:00
|
|
|
|
2014-01-19 02:20:44 +00:00
|
|
|
validates :config_changed, :inclusion => {:in => [0, 1]}
|
2014-01-17 04:51:19 +00:00
|
|
|
validates :hostname, presence: true
|
2014-01-19 02:20:44 +00:00
|
|
|
validates :fileserve, :inclusion => {:in => [0, 1]}, :if => lambda {|s| s.fileserve.present? }
|
2014-01-17 04:51:19 +00:00
|
|
|
validates :server_id, presence: true
|
|
|
|
|
|
2014-01-17 19:55:26 +00:00
|
|
|
validates :template, presence: true
|
2014-01-21 14:51:03 +00:00
|
|
|
validates :mount_template, presence: true
|
2014-01-17 04:51:19 +00:00
|
|
|
|
2014-01-19 02:20:44 +00:00
|
|
|
before_save :before_save, unless: lambda { skip_config_changed_flag }
|
|
|
|
|
before_save :sanitize_active_admin
|
|
|
|
|
after_save :after_save
|
|
|
|
|
|
|
|
|
|
def before_save
|
|
|
|
|
self.config_changed = 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def sanitize_active_admin
|
|
|
|
|
self.template_id = nil if self.template_id == ''
|
|
|
|
|
self.limit_id = nil if self.limit_id == ''
|
|
|
|
|
self.admin_auth_id = nil if self.admin_auth_id == ''
|
|
|
|
|
self.directory_id = nil if self.directory_id == ''
|
|
|
|
|
self.master_relay_id = nil if self.master_relay_id == ''
|
|
|
|
|
self.path_id = nil if self.path_id == ''
|
|
|
|
|
self.logging_id = nil if self.logging_id == ''
|
|
|
|
|
self.security_id = nil if self.security_id == ''
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def after_save
|
|
|
|
|
# if we set config_changed, then queue up a job
|
|
|
|
|
if config_changed_was == 0 && config_changed == 1
|
|
|
|
|
IcecastConfigWriter.enqueue(self.server_id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# this method is the correct way to set config_changed to false
|
|
|
|
|
# if you don't do it this way, then likely you'll get into a loop
|
|
|
|
|
# config_changed = true, enqueued job, job executes, job accidentally flags config_changed by touching the model, and repeat
|
|
|
|
|
def config_updated
|
|
|
|
|
self.skip_config_changed_flag = true
|
|
|
|
|
|
|
|
|
|
self.config_changed = 0
|
|
|
|
|
begin
|
|
|
|
|
self.save!
|
|
|
|
|
rescue
|
|
|
|
|
raise
|
|
|
|
|
ensure
|
|
|
|
|
self.skip_config_changed_flag = false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-01-21 14:51:03 +00:00
|
|
|
|
|
|
|
|
def pick_listen_socket(field)
|
|
|
|
|
current_listen_sockets = listen_sockets.length > 0 ? listen_sockets : template.listen_sockets
|
|
|
|
|
socket = current_listen_sockets.first
|
|
|
|
|
socket[:field] if socket
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# pick an icecast server with the least listeners * sources
|
|
|
|
|
def self.find_best_server_for_user(user)
|
|
|
|
|
chosen_server_id = nil
|
|
|
|
|
chosen_server_weight = nil
|
|
|
|
|
|
|
|
|
|
ActiveRecord::Base.connection_pool.with_connection do |connection|
|
|
|
|
|
result = connection.execute('select SUM(listeners), SUM(sourced::int), icecast_servers.id
|
|
|
|
|
FROM icecast_servers
|
|
|
|
|
LEFT JOIN icecast_mounts ON icecast_servers.id = icecast_mounts.icecast_server_id
|
|
|
|
|
WHERE icecast_server_group_id = \'' + user.icecast_server_group_id + '\'
|
|
|
|
|
GROUP BY icecast_servers.id;')
|
|
|
|
|
|
|
|
|
|
result.cmd_tuples.times do |i|
|
|
|
|
|
listeners = result.getvalue(i, 0).to_i
|
|
|
|
|
sourced = result.getvalue(i, 1).to_i
|
|
|
|
|
icecast_server_id = result.getvalue(i, 2)
|
|
|
|
|
|
|
|
|
|
# compute weight. source is much more intensive than listener, based on load tests again 2.3.0
|
|
|
|
|
# http://icecast.org/loadtest2.php
|
|
|
|
|
|
|
|
|
|
weight = sourced * 10 + listeners
|
|
|
|
|
|
|
|
|
|
if !chosen_server_id || (weight < chosen_server_weight)
|
|
|
|
|
chosen_server_id = icecast_server_id
|
|
|
|
|
chosen_server_weight = weight
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
IcecastServer.find(chosen_server_id) if chosen_server_id
|
|
|
|
|
end
|
|
|
|
|
|
2014-01-19 02:20:44 +00:00
|
|
|
def to_s
|
2014-01-21 14:51:03 +00:00
|
|
|
server_id
|
2014-01-19 02:20:44 +00:00
|
|
|
end
|
|
|
|
|
|
2014-01-17 04:51:19 +00:00
|
|
|
def dumpXml (output=$stdout, indent=1)
|
|
|
|
|
|
|
|
|
|
builder = ::Builder::XmlMarkup.new(:target => output, :indent => indent)
|
|
|
|
|
|
2014-01-19 02:20:44 +00:00
|
|
|
builder.tag! 'icecast' do |root|
|
2014-01-21 14:51:03 +00:00
|
|
|
root.tag! 'hostname', hostname
|
|
|
|
|
root.tag! 'server-id', server_id
|
|
|
|
|
root.tag! 'location', resolve_string(:location) if string_present?(:location)
|
|
|
|
|
root.tag! 'admin', resolve_string(:admin_email) if string_present?(:admin_email)
|
|
|
|
|
root.tag! 'fileserve', resolve_int(:fileserve) if int_present?(:fileserve)
|
|
|
|
|
|
|
|
|
|
resolve_association(:limit).dumpXml(builder) if association_present?(:limit)
|
|
|
|
|
resolve_association(:admin_auth).dumpXml(builder) if association_present?(:admin_auth)
|
|
|
|
|
resolve_association(:directory).dumpXml(builder) if association_present?(:directory)
|
|
|
|
|
resolve_association(:master_relay).dumpXml(builder) if association_present?(:master_relay)
|
|
|
|
|
resolve_association(:path).dumpXml(builder) if association_present?(:path)
|
|
|
|
|
resolve_association(:logging).dumpXml(builder) if association_present?(:logging)
|
|
|
|
|
resolve_association(:security).dumpXml(builder) if association_present?(:security)
|
2014-01-17 04:51:19 +00:00
|
|
|
|
2014-01-21 14:51:03 +00:00
|
|
|
current_listen_sockets = listen_sockets.length > 0 ? listen_sockets : template.listen_sockets
|
2014-01-17 19:55:26 +00:00
|
|
|
current_listen_sockets.each do |listen_socket|
|
|
|
|
|
listen_socket.dumpXml(builder)
|
2014-01-17 04:51:19 +00:00
|
|
|
end
|
|
|
|
|
|
2014-01-17 19:55:26 +00:00
|
|
|
relays.each do |relay|
|
|
|
|
|
relay.dumpXml(builder)
|
2014-01-17 04:51:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
mounts.each do |mount|
|
|
|
|
|
mount.dumpXml(builder)
|
|
|
|
|
end
|
2014-01-14 21:22:05 +00:00
|
|
|
end
|
2014-01-10 21:02:52 +00:00
|
|
|
end
|
2014-01-21 14:51:03 +00:00
|
|
|
|
|
|
|
|
def resolve_string(field)
|
|
|
|
|
self[field].present? ? self[field] : template && template[field]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def string_present?(field)
|
|
|
|
|
val = resolve_string(field)
|
|
|
|
|
val ? val.present? : false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def resolve_int(field)
|
|
|
|
|
self[field] ? self[field]: template && template[field]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def int_present?(field)
|
|
|
|
|
resolve_int(field)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def resolve_association(field)
|
|
|
|
|
self.send(field) ? self.send(field) : template && template.send(field)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def association_present?(field)
|
|
|
|
|
resolve_association(field)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2014-01-07 20:29:40 +00:00
|
|
|
end
|
|
|
|
|
end
|