Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/bundler/puma-6.3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
farhatahmad authored Apr 22, 2024
2 parents 0208f2f + dd6fb56 commit 6e19cd8
Show file tree
Hide file tree
Showing 19 changed files with 726 additions and 67 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ To help users who are behind restrictive firewalls to send/receive media (audio,

Again, [bbb-install.sh](https://github.com/bigbluebutton/bbb-install#install-a-turn-server) can automate this process for you.

If you want Scalelite to infer and display the BBB version of your servers, you need to add the following line to `/etc/bigbluebutton/bbb-web.properties` on your BBB servers:
```
allowRevealOfBBBVersion=true
```

### Setup a shared volume for recordings

See [Setting up a shared volume for recordings](docs/sharedvolume-README.md)
Expand Down
15 changes: 12 additions & 3 deletions app/controllers/api/servers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class ServersController < ApplicationController
# "id": String,
# "url": String,
# "secret": String,
# "tag": String,
# "bbb_version": String,
# "state": String,
# "load": String,
# "load_multiplier": String,
Expand Down Expand Up @@ -49,6 +51,8 @@ def get_servers
# "id": String,
# "url": String,
# "secret": String,
# "tag": String,
# "bbb_version": String,
# "state": String,
# "load": String,
# "load_multiplier": String,
Expand All @@ -71,6 +75,7 @@ def get_server_info
# "url": String, # Required: URL of the BigBlueButton server
# "secret": String, # Required: Secret key of the BigBlueButton server
# "load_multiplier": Float # Optional: A non-zero number, defaults to 1.0 if not provided or zero
# "tag": String # Optional: A special-purpose tag for the server (empty String to not set it)
# }
# }
def add_server
Expand All @@ -80,7 +85,8 @@ def add_server
tmp_load_multiplier = server_create_params[:load_multiplier].presence&.to_d || 1.0
tmp_load_multiplier = 1.0 if tmp_load_multiplier.zero?

server = Server.create!(url: server_create_params[:url], secret: server_create_params[:secret], load_multiplier: tmp_load_multiplier)
server = Server.create!(url: server_create_params[:url], secret: server_create_params[:secret],
load_multiplier: tmp_load_multiplier, tag: server_create_params[:tag].presence)
render json: server_to_json(server), status: :created
end
end
Expand All @@ -95,6 +101,7 @@ def add_server
# "state": String, # Optional: 'enable', 'cordon', or 'disable'
# "load_multiplier": Float # Optional: A non-zero number
# "secret": String # Optional: Secret key of the BigBlueButton server
# "tag": String # Optional: A special-purpose tag for the server, empty string to remove the tag
# }
# }
def update_server
Expand Down Expand Up @@ -166,6 +173,8 @@ def server_to_json(server)
id: server.id,
url: server.url,
secret: server.secret,
tag: server.tag.nil? ? '' : server.tag,
bbb_version: server.bbb_version.nil? ? '' : server.bbb_version,
state: server.state.presence || (server.enabled ? 'enabled' : 'disabled'),
load: server.load.presence || 'unavailable',
load_multiplier: server.load_multiplier.nil? ? 1.0 : server.load_multiplier.to_d,
Expand All @@ -174,11 +183,11 @@ def server_to_json(server)
end

def server_create_params
params.require(:server).permit(:url, :secret, :load_multiplier)
params.require(:server).permit(:url, :secret, :load_multiplier, :tag)
end

def server_update_params
params.require(:server).permit(:state, :load_multiplier, :secret)
params.require(:server).permit(:state, :load_multiplier, :secret, :tag)
end

def server_panic_params
Expand Down
63 changes: 38 additions & 25 deletions app/controllers/bigbluebutton_api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,44 +160,54 @@ def create
params.require(:meetingID)

begin
server = Server.find_available
# Check if meeting is already running
meeting = Meeting.find(params[:meetingID], @tenant&.id)
server = meeting.server
logger.debug("Found existing meeting #{params[:meetingID]} on BigBlueButton server #{server.id}.")
rescue ApplicationRedisRecord::RecordNotFound
raise InternalError, 'Could not find any available servers.'
begin
# Find available server and create meeting on it
server = Server.find_available(params[:'meta_server-tag'])

# Create meeting in database
logger.debug("Creating meeting #{params[:meetingID]} in database.")
moderator_pwd = params[:moderatorPW].presence || SecureRandom.alphanumeric(8)
meeting = Meeting.find_or_create_with_server!(
params[:meetingID],
server,
moderator_pwd,
params[:voiceBridge],
@tenant&.id
)

# Update server if meeting (unexpectedly) already existed on a different server
server = meeting.server

logger.debug("Incrementing server #{server.id} load by 1")
server.increment_load(1)
rescue ApplicationRedisRecord::RecordNotFound => e
raise InternalError, e.message
end
end

# Create meeting in database
logger.debug("Creating meeting #{params[:meetingID]} in database.")

moderator_pwd = params[:moderatorPW].presence || SecureRandom.alphanumeric(8)
params[:moderatorPW] = moderator_pwd

meeting = Meeting.find_or_create_with_server!(
params[:meetingID],
server,
moderator_pwd,
params[:voiceBridge],
@tenant&.id
)

# Update with old server if meeting already existed in database
server = meeting.server

logger.debug("Incrementing server #{server.id} load by 1")
server.increment_load(1)
params[:moderatorPW] = meeting.moderator_pw
params[:voiceBridge] = meeting.voice_bridge
params[:'meta_tenant-id'] = @tenant.id if @tenant.present?
if server.tag.present?
params[:'meta_server-tag'] = server.tag
else
params.delete(:'meta_server-tag')
end

duration = params[:duration].to_i

params[:'meta_tenant-id'] = @tenant.id if @tenant.present?

# Set/Overite duration if MAX_MEETING_DURATION is set and it's greater than params[:duration] (if passed)
if !Rails.configuration.x.max_meeting_duration.zero? &&
(duration.zero? || duration > Rails.configuration.x.max_meeting_duration)
logger.debug("Setting duration to #{Rails.configuration.x.max_meeting_duration}")
params[:duration] = Rails.configuration.x.max_meeting_duration
end

params[:voiceBridge] = meeting.voice_bridge

if @tenant&.lrs_endpoint.present?
lrs_payload = LrsPayloadService.new(tenant: @tenant, secret: server.secret).call
params[:'meta_secret-lrs-payload'] = lrs_payload if lrs_payload.present?
Expand Down Expand Up @@ -289,6 +299,9 @@ def join
logger.info("The requested meeting #{params[:meetingID]} does not exist")
raise MeetingNotFoundError
end
logger.debug("Incrementing server #{server.id} load by 1")
server.increment_load(1)

# Get list of params that should not be modified by join API call
excluded_params = Rails.configuration.x.join_exclude_params

Expand Down
40 changes: 31 additions & 9 deletions app/models/server.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# frozen_string_literal: true

class Server < ApplicationRedisRecord
define_attribute_methods :id, :url, :secret, :enabled, :load, :online, :load_multiplier, :healthy_counter,
:unhealthy_counter, :state, :meetings, :users, :largest_meeting, :videos
define_attribute_methods :id, :url, :secret, :tag, :enabled, :load, :online, :load_multiplier, :healthy_counter,
:unhealthy_counter, :state, :meetings, :users, :largest_meeting, :videos, :bbb_version

# Unique ID for this server
application_redis_attr :id
Expand All @@ -13,6 +13,9 @@ class Server < ApplicationRedisRecord
# Shared secret for making API requests to this server
application_redis_attr :secret

# Special purpose tag for this server
application_redis_attr :tag

# Whether the server is administratively enabled (allowed to create new meetings)
application_redis_attr :enabled

Expand Down Expand Up @@ -46,6 +49,9 @@ class Server < ApplicationRedisRecord
# Indicator of total video streams in this server
application_redis_attr :videos

# Indicator of the BBB version of this server
application_redis_attr :bbb_version

def online=(value)
value = !!value
online_will_change! unless @online == value
Expand Down Expand Up @@ -91,13 +97,17 @@ def save!
result = redis.multi do |pipeline|
pipeline.hset(server_key, 'url', url) if url_changed?
pipeline.hset(server_key, 'secret', secret) if secret_changed?
if tag_changed?
tag.present? ? pipeline.hset(server_key, 'tag', tag) : pipeline.hdel(server_key, 'tag')
end
pipeline.hset(server_key, 'online', online ? 'true' : 'false') if online_changed?
pipeline.hset(server_key, 'load_multiplier', load_multiplier) if load_multiplier_changed?
pipeline.hset(server_key, 'state', state) if state_changed?
pipeline.hset(server_key, 'meetings', meetings) if meetings_changed?
pipeline.hset(server_key, 'users', users) if users_changed?
pipeline.hset(server_key, 'largest_meeting', largest_meeting) if largest_meeting_changed?
pipeline.hset(server_key, 'videos', videos) if videos_changed?
pipeline.hset(server_key, 'bbb_version', bbb_version) if bbb_version_changed?
pipeline.sadd?('servers', id) if id_changed?
state.present? ? save_with_state(pipeline) : save_without_state(pipeline)
end
Expand Down Expand Up @@ -276,17 +286,29 @@ def self.find(id)
end

# Find the server with the lowest load (for creating a new meeting)
def self.find_available
with_connection do |redis|
id, load, hash = 5.times do
ids_loads = redis.zrange('server_load', 0, 0, with_scores: true)
raise RecordNotFound.new("Couldn't find available Server", name, nil) if ids_loads.blank?
def self.find_available(tag_arg = nil)
# Check if tag is required
tag = tag_arg.presence
tag_required = false
if !tag.nil? && tag[-1] == '!'
tag = tag[0..-2].presence # always returns String, if tag is String
tag_required = true
end

id, load = ids_loads.first
# Find available&matching server with the lowest load
with_connection do |redis|
ids_loads = redis.zrange('server_load', 0, -1, with_scores: true)
raise RecordNotFound.new("Could not find any available servers.", name, nil) if ids_loads.blank?
if !tag.nil? && ids_loads.none? { |myid, _| redis.hget(key(myid), 'tag') == tag }
raise RecordNotFound.new("Could not find any available servers with tag=#{tag}.", name, nil) if tag_required
tag = nil # fall back to servers without tag
end
ids_loads = ids_loads.select { |myid, _| redis.hget(key(myid), 'tag') == tag }
id, load, hash = ids_loads.each do |id, load|
hash = redis.hgetall(key(id))
break id, load, hash if hash.present?
end
raise RecordNotFound.new("Couldn't find available Server", name, id) if hash.blank?
raise RecordNotFound.new("Could not find any available servers.", name, id) if hash.blank?

hash['id'] = id
if hash['state'].present?
Expand Down
6 changes: 6 additions & 0 deletions app/services/server_update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def call

update_secret if @params[:secret].present?

update_tag unless @params[:tag].nil?

@server.save!
end

Expand Down Expand Up @@ -43,4 +45,8 @@ def update_load_multiplier
def update_secret
@server.secret = @params[:secret]
end

def update_tag
@server.tag = @params[:tag].presence
end
end
4 changes: 4 additions & 0 deletions bigbluebutton/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@ Finally (after switching back to root), set the `spool_dir` setting in `scalelit
### Other configurations

If you need to customize the rsync command (for example, to pass the `--rsh` option to set up a tunnel), you can add extra rsync command line arguments via the `extra_rsync_opts` array in `scalelite.yml`.

### Recording cleanup cronjob

Copy the script `scalelite_prune_recordings` to `/etc/cron.daily` on the BBB server, to periodically clean up the local files of recordings that have been transferred to Scalelite. You may adjust the variables MAXAGE and EVENTS_MAXAGE to the number of days you would like to keep the local files on the BBB server.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
#!/bin/bash

# Place this file in /etc/cron.daily
# Place this file in /etc/cron.daily and make sure it is executable
#

# Delete sent recording after 4 days
Expand Down
10 changes: 8 additions & 2 deletions docs/api-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ n/a
"id": String,
"url": String,
"secret": String,
"tag": String,
"state": String,
"load": String,
"load_multiplier": String,
Expand Down Expand Up @@ -56,6 +57,7 @@ Returns the data associated with a single server
"id": String,
"url": String,
"secret": String,
"tag": String,
"state": String,
"load": String,
"load_multiplier": String,
Expand Down Expand Up @@ -83,7 +85,8 @@ Adds a new server
"server": {
"url": String, # Required: URL of the BigBlueButton server
"secret": String, # Required: Secret key of the BigBlueButton server
"load_multiplier": Float # Optional: A non-zero number, defaults to 1.0 if not provided or zero
"load_multiplier": Float, # Optional: A non-zero number, defaults to 1.0 if not provided or zero
"tag": String # Optional: A special-purpose tag for the server (empty string to not set it)
}
}
```
Expand All @@ -94,6 +97,7 @@ Adds a new server
"id": String,
"url": String,
"secret": String,
"tag": String,
"state": String,
"load": String,
"load_multiplier": String,
Expand Down Expand Up @@ -122,7 +126,8 @@ Updates a server
"server": {
"state": String, # Optional: 'enable', 'cordon', or 'disable'
"load_multiplier": Float # Optional: A non-zero number
"secret": String # Optional: Secret key of the BigBlueButton server
"secret": String, # Optional: Secret key of the BigBlueButton server
"tag": String # Optional: A special-purpose tag for the server (empty string to remove the tag)
}
}
```
Expand All @@ -133,6 +138,7 @@ Updates a server
"id": String,
"url": String,
"secret": String,
"tag": String,
"state": String,
"load": String,
"load_multiplier": String,
Expand Down
Loading

0 comments on commit 6e19cd8

Please sign in to comment.