RaP: Clean up resque worker code, add worker for keep_events feature

I've moved the workers code into the `lib` subdirectory with other library-ish
code; this puts it into the ruby load path used by most scripts so referencing
files is easier.

I've applied various style cleanups based on the rubocop config present.

The `events` processing step has been integrated as a new worker `EventsWorker`,
there is no longer a separate `events/events.rb` script. I've reworked the
`rap-starter.rb` script to check for the done files in both the events and
recorded status directories.
This commit is contained in:
Calvin Walton 2020-01-21 17:14:39 -05:00
parent bc1b53f037
commit c52a67186e
16 changed files with 219 additions and 352 deletions

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
# Copyright © 2019 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
# BigBlueButton is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
# Set up the load path for requiring recordandplayback modules
$LOAD_PATH.unshift(__dir__) unless $LOAD_PATH.include?(__dir__)

4
record-and-playback/core/lib/recordandplayback.rb Executable file → Normal file
View File

@ -19,9 +19,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
path = File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
$LOAD_PATH << path
require_relative 'boot'
require 'recordandplayback/events_archiver'
require 'recordandplayback/generators/events'

View File

@ -1,6 +1,6 @@
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -17,9 +17,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../rap-base-worker.rb', __FILE__)
require File.expand_path('../rap-archive-worker.rb', __FILE__)
require File.expand_path('../rap-sanity-worker.rb', __FILE__)
require File.expand_path('../rap-process-worker.rb', __FILE__)
require File.expand_path('../rap-publish-worker.rb', __FILE__)
require File.expand_path('../rap-captions-worker.rb', __FILE__)
require 'recordandplayback/workers/base_worker'
require 'recordandplayback/workers/archive_worker'
require 'recordandplayback/workers/captions_worker'
require 'recordandplayback/workers/events_worker'
require 'recordandplayback/workers/process_worker'
require 'recordandplayback/workers/publish_worker'
require 'recordandplayback/workers/sanity_worker'

View File

@ -1,6 +1,6 @@
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -17,8 +17,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../workers', __FILE__)
module BigBlueButton
module Resque
class ArchiveWorker < BaseWorker
@ -31,7 +29,7 @@ module BigBlueButton
remove_status_files
script = File.expand_path('../../archive/archive.rb', __FILE__)
script = File.expand_path('../archive/archive.rb', __dir__)
if @break_timestamp.nil?
ret, step_time = run_script(script, '-m', @meeting_id)
else
@ -44,11 +42,7 @@ module BigBlueButton
!File.exist?(@archived_fail)
)
@publisher.put_archive_ended(
@meeting_id, {
success: step_succeeded,
step_time: step_time,
})
@publisher.put_archive_ended(@meeting_id, success: step_succeeded, step_time: step_time)
if step_succeeded
@logger.info("Successfully archived #{@full_id}")
@ -78,4 +72,3 @@ module BigBlueButton
end
end
end

View File

@ -1,7 +1,6 @@
#!/usr/bin/ruby
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -18,8 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../../../lib/recordandplayback', __FILE__)
require File.expand_path('../workers', __FILE__)
require 'recordandplayback'
require 'rubygems'
require 'yaml'
require 'fileutils'
@ -44,12 +42,10 @@ module BigBlueButton
success = yield
@logger.info("Ended worker #{@step_name} for #{@meeting_id} with result #{success}")
if success
schedule_next_step unless @single_step
else
raise "Worker #{@step_name} for #{@meeting_id} failed with result #{success}"
end
rescue Exception => e
raise "Worker #{@step_name} for #{@meeting_id} failed with result #{success}" unless success
schedule_next_step unless @single_step
rescue StandardError => e
@logger.error(e.message)
e.backtrace.each do |traceline|
@logger.error(traceline)
@ -62,8 +58,8 @@ module BigBlueButton
Dir.glob(glob).sort.each do |post_script|
match = %r{([^/]*).rb$}.match(post_script)
post_type = match[1]
@logger.info("Running post #{@step_name} script #{post_type}")
@logger.info("Running post #{@step_name} script #{post_type}")
post_started_method(post_type, @meeting_id)
if @format_name.nil?
@ -73,15 +69,8 @@ module BigBlueButton
end
step_succeeded = ret.zero?
post_ended_method(
post_type, @meeting_id, {
success: step_succeeded,
step_time: step_time,
})
unless step_succeeded
@logger.warn("Post #{@step_name} script #{post_script}/#{post_type} failed")
end
@logger.warn("Post #{@step_name} script #{post_script}/#{post_type} failed") unless step_succeeded
post_ended_method(post_type, @meeting_id, success: step_succeeded, step_time: step_time)
end
end
@ -147,12 +136,12 @@ module BigBlueButton
end
def initialize(opts)
props = BigBlueButton.read_props
@props = BigBlueButton.read_props
BigBlueButton.create_redis_publisher
@publisher = BigBlueButton.redis_publisher
@log_dir = props['log_dir']
@recording_dir = props['recording_dir']
@log_dir = @props['log_dir']
@recording_dir = @props['recording_dir']
@meeting_id = opts['meeting_id']
@break_timestamp = opts['break_timestamp']
@single_step = opts['single_step'] || false

View File

@ -1,6 +1,6 @@
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -17,8 +17,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../workers', __FILE__)
module BigBlueButton
module Resque
class CaptionsWorker < BaseWorker

View File

@ -0,0 +1,76 @@
# frozen_string_literal: true
# Copyright © 2019 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
# BigBlueButton is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
module BigBlueButton
module Resque
class EventsWorker < BaseWorker
@queue = 'rap:archive'
def store_events(target_dir)
@logger.info("Keeping events for #{meeting_id}")
redis = BigBlueButton::RedisWrapper.new(@props['redis_host'], @props['redis_port'], @props['redis_password'])
events_archiver = BigBlueButton::RedisEventsArchiver.new(redis)
events_xml = "#{target_dir}/events.xml"
events_archiver.store_events(@meeting_id, events_xml, @break_timestamp)
# When the events script is responsible for archiving events, the sanity script (which normally clears the events
# from redis) will not get run, so we have to clear them here.
events_archiver.delete_events(meeting_id) if @break_timestamp.nil?
end
def perform
super do
@logger.info("Running events worker for #{@full_id}")
unless File.exist(@ended_done)
@logger.info("Events generation was not enabled for #{@full_id}, skipping")
next true # We're inside a block and want to return to the block's caller, not return from perform
end
target_dir = "#{@events_dir}/#{@meeting_id}"
FileUtils.mkdir_p(target_dir)
if File.exist?(raw_events_xml)
# This is a recorded meeting. The archive step should have already run, so copy the events.xml from the raw
# recording directory.
FileUtils.cp("#{@recording_dir}/raw/#{@meeting_id}/events.xml", "#{target_dir}/events.xml")
else
# This meeting was not recorded. We need to run the (incremental, if break_timestamp was provided) events archiving.
store_events(target_dir)
end
FileUtils.rm_f(@ended_done)
# Only run post events scripts after full meeting ends, not during segments
run_post_scripts(@post_scripts_path) if @break_timestamp.nil?
@logger.info("Finished events worker for #{@full_id}")
end
end
def initialize(opts)
super(opts)
@step_name = 'events'
@events_dir = @props['events_dir']
@post_scripts_path = File.expand_path('../post_events', __dir__)
@ended_done = "#{@recording_dir}/status/ended/#{@full_id}.done"
end
end
end
end

View File

@ -1,6 +1,6 @@
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -17,8 +17,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../workers', __FILE__)
module BigBlueButton
module Resque
class ProcessWorker < BaseWorker
@ -48,11 +46,7 @@ module BigBlueButton
File.exist?(@processed_done) && !File.exist?(@processed_fail)
)
@publisher.put_process_ended(
@format_name, @meeting_id, {
success: step_succeeded,
step_time: step_time,
})
@publisher.put_process_ended(@format_name, @meeting_id, success: step_succeeded, step_time: step_time)
if step_succeeded
@logger.info("Process format succeeded for #{@full_id}:#{@format_name}")
@ -85,11 +79,10 @@ module BigBlueButton
super(opts)
@step_name = 'process'
@format_name = opts['format_name']
@post_scripts_path = File.expand_path('../../post_process', __FILE__)
@post_scripts_path = File.expand_path('../post_process', __dir__)
@processed_done = "#{@recording_dir}/status/processed/#{@meeting_id}-#{@format_name}.done"
@processed_fail = "#{@recording_dir}/status/processed/#{@meeting_id}-#{@format_name}.fail"
end
end
end
end

View File

@ -1,6 +1,6 @@
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -17,7 +17,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../workers', __FILE__)
require 'custom_hash'
module BigBlueButton
@ -49,8 +48,7 @@ module BigBlueButton
File.exist?(@published_done) && !File.exist?(@published_fail)
)
props = BigBlueButton.read_props
published_dir = props['published_dir']
published_dir = @props['published_dir']
playback = {}
metadata = {}
@ -68,7 +66,7 @@ module BigBlueButton
raw_size = doc[:recording][:raw_size] unless doc[:recording][:raw_size].nil?
start_time = doc[:recording][:start_time] unless doc[:recording][:start_time].nil?
end_time = doc[:recording][:end_time] unless doc[:recording][:end_time].nil?
rescue Exception => e
rescue StandardError => e
BigBlueButton.logger.warn 'An exception occurred while loading the extra information for the publish event'
BigBlueButton.logger.warn e.message
e.backtrace.each do |traceline|
@ -80,16 +78,16 @@ module BigBlueButton
end
@publisher.put_publish_ended(
@format_name, @meeting_id, {
'success': step_succeeded,
'step_time': step_time,
'playback': playback,
'metadata': metadata,
'download': download,
'raw_size': raw_size,
'start_time': start_time,
'end_time': end_time,
})
@format_name, @meeting_id,
'success': step_succeeded,
'step_time': step_time,
'playback': playback,
'metadata': metadata,
'download': download,
'raw_size': raw_size,
'start_time': start_time,
'end_time': end_time
)
else
@logger.warn("Processed recording found for #{@meeting_id}/#{@format_name}, but no publish script exists")
step_succeeded = true
@ -119,11 +117,10 @@ module BigBlueButton
super(opts)
@step_name = 'publish'
@format_name = opts['format_name']
@post_scripts_path = File.expand_path('../../post_publish', __FILE__)
@post_scripts_path = File.expand_path('../post_publish', __dir__)
@published_done = "#{@recording_dir}/status/published/#{@meeting_id}-#{@format_name}.done"
@published_fail = "#{@recording_dir}/status/published/#{@meeting_id}-#{@format_name}.fail"
end
end
end
end

View File

@ -1,6 +1,6 @@
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -17,8 +17,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../workers', __FILE__)
module BigBlueButton
module Resque
class SanityWorker < BaseWorker
@ -31,7 +29,7 @@ module BigBlueButton
remove_status_files
script = File.expand_path('../../sanity/sanity.rb', __FILE__)
script = File.expand_path('../sanity/sanity.rb', __dir__)
if @break_timestamp.nil?
ret, step_time = run_script(script, '-m', @meeting_id)
else
@ -39,11 +37,7 @@ module BigBlueButton
end
step_succeeded = (ret.zero? && File.exist?(@sanity_done))
@publisher.put_sanity_ended(
@meeting_id, {
success: step_succeeded,
step_time: step_time,
})
@publisher.put_sanity_ended(@meeting_id, success: step_succeeded, step_time: step_time)
if step_succeeded
@logger.info("Successfully sanity checked #{@full_id}")
@ -65,7 +59,7 @@ module BigBlueButton
def initialize(opts)
super(opts)
@step_name = 'sanity'
@post_scripts_path = File.expand_path('../../post_archive', __FILE__)
@post_scripts_path = File.expand_path('../post_archive', __dir__)
@sanity_fail = "#{@recording_dir}/status/sanity/#{@meeting_id}.fail"
@sanity_done = "#{@recording_dir}/status/sanity/#{@meeting_id}.done"
end

View File

@ -1,98 +0,0 @@
# Set encoding to utf-8
# encoding: UTF-8
#
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3.0 of the License, or (at your option)
# any later version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#
require '../lib/recordandplayback'
require 'logger'
require 'trollop'
require 'yaml'
def keep_events(meeting_id, redis_host, redis_port, redis_password, events_dir, break_timestamp)
BigBlueButton.logger.info("Keeping events for #{meeting_id}")
redis = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password)
events_archiver = BigBlueButton::RedisEventsArchiver.new redis
target_dir = "#{events_dir}/#{meeting_id}"
if not FileTest.directory?(target_dir)
FileUtils.mkdir_p target_dir
end
events_xml = "#{target_dir}/events.xml"
events = events_archiver.store_events(meeting_id, events_xml, break_timestamp)
end
################## START ################################
opts = Trollop::options do
opt :meeting_id, "Meeting id with events to keep", type: :string
opt :break_timestamp, "Chapter break end timestamp", type: :integer
end
Trollop::die :meeting_id, "must be provided" if opts[:meeting_id].nil?
meeting_id = opts[:meeting_id]
break_timestamp = opts[:break_timestamp]
# This script lives in scripts/archive/steps while bigbluebutton.yml lives in scripts/
props = YAML::load(File.open('bigbluebutton.yml'))
recording_dir = props['recording_dir']
raw_archive_dir = "#{recording_dir}/raw"
events_dir = props['events_dir']
redis_host = props['redis_host']
redis_port = props['redis_port']
redis_password = props['redis_password']
log_dir = props['log_dir']
raw_events_xml = "#{raw_archive_dir}/#{meeting_id}/events.xml"
ended_done_file = "#{recording_dir}/status/ended/#{meeting_id}.done"
recorded_done_file = "#{recording_dir}/status/recorded/#{meeting_id}.done"
BigBlueButton.logger = Logger.new("#{log_dir}/events.log", 'daily')
# Be aware of this section! This is used as a synchronization between
# rap-archive-worker and rap-events-worker for meetings that are recorded.
# We want the archive step to finish to make sure that the events.xml
# has already been written in raw directory before copying into the events
# directory. (ralam July 5, 2019)
if File.exist? recorded_done_file
BigBlueButton.logger.info("Temporarily skipping #{meeting_id} for archive to finish")
exit 0
end
target_dir = "#{events_dir}/#{meeting_id}"
if not FileTest.directory?(target_dir)
FileUtils.mkdir_p target_dir
if File.exist? raw_events_xml
# This is a recorded meetings. Therefore, copy the events.xml
# from raw directory instead of collecting the events from redis.
# (ralam July 5, 2019)
BigBlueButton.logger.info("Copying events from #{raw_events_xml}")
events_xml = "#{events_dir}/#{meeting_id}/events.xml"
FileUtils.cp(raw_events_xml, events_xml)
else
keep_events(meeting_id, redis_host, redis_port, redis_password, events_dir, break_timestamp)
# we need to delete the keys here because the sanity phase might not
# automatically happen for this recording
BigBlueButton.logger.info("Deleting redis keys")
redis = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password)
events_archiver = BigBlueButton::RedisEventsArchiver.new(redis)
events_archiver.delete_events(meeting_id)
end
FileUtils.rm ended_done_file
end

View File

@ -1,97 +0,0 @@
#!/usr/bin/ruby
# encoding: utf-8
# Copyright ⓒ 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
# BigBlueButton is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require '../lib/recordandplayback'
require 'rubygems'
require 'yaml'
require 'fileutils'
def run_post_events_script(meeting_id)
Dir.glob('post_events/*.rb').sort.each do |post_event_script|
match = %r{([^/]*).rb$}.match(post_event_script)
post_type = match[1]
BigBlueButton.logger.info("Running post event script #{post_type}")
# step_start_time = BigBlueButton.monotonic_clock
ret = BigBlueButton.exec_ret('ruby', post_event_script, '-m', meeting_id)
# step_stop_time = BigBlueButton.monotonic_clock
# step_time = step_stop_time - step_start_time
step_succeeded = ret.zero?
unless step_succeeded
BigBlueButton.logger.warn("Post event script #{post_event_script} failed")
end
end
end
def keep_meeting_events(recording_dir, ended_done_file)
ended_done_base = File.basename(ended_done_file, '.done')
meeting_id = nil
break_timestamp = nil
if match = /^([0-9a-f]+-[0-9]+)$/.match(ended_done_base)
meeting_id = match[1]
elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(ended_done_base)
meeting_id = match[1]
break_timestamp = match[2]
else
BigBlueButton.logger.warn("Ended done file for #{ended_done_base} has invalid format")
end
return if meeting_id.nil?
if !break_timestamp.nil?
BigBlueButton.exec_ret('ruby', 'events/events.rb', '-m', meeting_id, '-b', break_timestamp)
else
BigBlueButton.exec_ret('ruby', 'events/events.rb', '-m', meeting_id)
# Need to only run post events scripts after meeting ends not during segments.
run_post_events_script(meeting_id)
end
end
begin
props = YAML.safe_load(File.open('bigbluebutton.yml'))
redis_host = props['redis_host']
redis_port = props['redis_port']
redis_password = props['redis_password']
BigBlueButton.redis_publisher = BigBlueButton::RedisWrapper.new(redis_host, redis_port, redis_password)
log_dir = props['log_dir']
recording_dir = props['recording_dir']
logger = Logger.new("#{log_dir}/bbb-rap-worker.log")
# logger = Logger.new(STDOUT)
logger.level = Logger::INFO
BigBlueButton.logger = logger
BigBlueButton.logger.info('Running rap-events-worker...')
ended_done_files = Dir.glob("#{recording_dir}/status/ended/*.done")
ended_done_files.each do |ended_done|
keep_meeting_events(recording_dir, ended_done)
end
BigBlueButton.logger.debug('rap-events-worker done')
rescue Exception => e
BigBlueButton.logger.error(e.message)
e.backtrace.each do |traceline|
BigBlueButton.logger.error(traceline)
end
end

View File

@ -1,7 +1,7 @@
#!/usr/bin/ruby
# encoding: utf-8
# frozen_string_literal: true
# Copyright 2017 BigBlueButton Inc. and by respective authors.
# Copyright © 2017 BigBlueButton Inc. and by respective authors.
#
# This file is part of BigBlueButton open source conferencing system.
#
@ -18,8 +18,9 @@
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton. If not, see <http://www.gnu.org/licenses/>.
require File.expand_path('../../lib/recordandplayback', __FILE__)
require File.expand_path('../workers/workers', __FILE__)
require_relative '../lib/recordandplayback'
require 'recordandplayback/workers'
require 'rubygems'
require 'yaml'
require 'fileutils'
@ -29,72 +30,96 @@ require 'rb-inotify'
class StopSignalException < StandardError
end
def archive_recorded_meetings(recording_dir, done_file)
FileUtils.mkdir_p("#{recording_dir}/status/archived")
meeting_id = nil
break_timestamp = nil
if match = /^([0-9a-f]+-[0-9]+)$/.match(done_file)
meeting_id = match[1]
elsif match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(done_file)
meeting_id = match[1]
break_timestamp = match[2]
def parse_meeting_id(done_file)
id = File.basename(done_file, '.done')
if (match = /^([0-9a-f]+-[0-9]+)$/.match(id))
{ meeting_id: match[1], break_timestamp: nil }
elsif (match = /^([0-9a-f]+-[0-9]+)-([0-9]+)$/.match(id))
{ meeting_id: match[1], break_timestamp: match[2] }
else
BigBlueButton.logger.warn("Recording done file for #{done_file} has invalid format")
BigBlueButton.logger.warn("Recording done file #{done_file} has invalid format")
nil
end
end
def keep_meeting_events(recording_dir, ended_done_file)
id = File.basename(ended_done_file, '.done')
BigBlueButton.logger.debug("Seen new ended done file for #{id}")
attrs = parse_meeting_id(id)
return if attrs.nil?
# If a meeting was recorded, the events will be archived by the archive step, and
# the events script will be run after that (it'll grab the already archived events
# from the recording raw directory)
if File.exist?("#{recording_dir}/status/recorded/#{id}.done") || File.exist?("#{recording_dir}/raw/#{attrs[:meeting_id]}")
BigBlueButton.logger.info("Meeting #{id} had recording enabled, events will be handled after recording archive")
return
end
attrs = {
'meeting_id': meeting_id,
'break_timestamp': break_timestamp,
}
BigBlueButton.logger.info("Enqueueing job to keep events #{attrs.inspect}")
Resque.enqueue(BigBlueButton::Resque::EventsWorker, attrs)
FileUtils.rm_f(ended_done_file)
end
def archive_recorded_meetings(recording_dir, done_file)
id = File.basename(done_file, '.done')
BigBlueButton.logger.debug("Seen new recorded done file for #{id}")
attrs = parse_meeting_id(id)
return if attrs.nil?
# The keep events stuff uses this directory presence to check if a meeting is
# recorded when the recorded done file is gone
FileUtils.mkdir_p("#{recording_dir}/raw/#{attrs[:meeting_id]}")
BigBlueButton.logger.info("Enqueuing job to archive #{attrs.inspect}")
Resque.enqueue(BigBlueButton::Resque::ArchiveWorker, attrs)
FileUtils.rm_f(recorded_done_file)
end
begin
props = BigBlueButton.read_props
log_dir = props['log_dir']
redis_host = props['redis_workers_host'] || props['redis_host']
redis_port = props['redis_workers_port'] || props['redis_port']
Resque.redis = "#{redis_host}:#{redis_port}"
BigBlueButton.logger.debug('Running rap-trigger...')
BigBlueButton.logger.debug('Running rap-starter...')
recording_dir = props['recording_dir']
watch_dir = "#{recording_dir}/status/recorded/"
ended_status_dir = "#{recording_dir}/status/ended"
recorded_status_dir = "#{recording_dir}/status/recorded"
# start the process for all .done files already there
done_files = Dir.glob("#{watch_dir}*.done")
done_files.each do |file|
id = File.basename(file, '.done')
BigBlueButton.logger.info "Detected recording #{id}, starting the processing"
archive_recorded_meetings(recording_dir, id)
FileUtils.rm_f(file)
# Check for missed "ended" .done files
ended_done_files = Dir.glob("#{ended_status_dir}/*.done")
ended_done_files.each do |ended_done_file|
keep_meeting_events(recording_id, ended_done_file)
end
# Listen the directory for when new files are created
BigBlueButton.logger.info("Setting up inotify watch on #{watch_dir}")
# Check for missed "recorded" .done files
recorded_done_files = Dir.glob("#{recorded_status_dir}/*.done")
recorded_done_files.each do |recorded_done_file|
archive_recorded_meetings(recording_dir, recorded_done_file)
end
# Listen the directories for when new files are created
BigBlueButton.logger.info("Setting up inotify watch on #{ended_status_dir}")
notifier = INotify::Notifier.new
notifier.watch(watch_dir, :moved_to, :create) do |event|
notifier.watch(ended_status_dir, :moved_to, :create) do |event|
next unless event.name.end_with?('.done')
id = File.basename(event.name, '.done')
BigBlueButton.logger.info "Detected recording #{id}, starting the processing"
archive_recorded_meetings(recording_dir, id)
FileUtils.rm_f(event.absolute_name)
keep_meeting_events(recording_dir, event.absolute_name)
end
BigBlueButton.logger.info("Setting up inotify watch on #{recorded_status_dir}")
notifier.watch(recorded_status_dir, :moved_to, :create) do |event|
next unless event.name.end_with?('.done')
archive_recorded_meetings(recording_dir, event.absolute_name)
end
BigBlueButton.logger.info('Waiting for new recordings...')
Signal.trap('INT') { raise StopSignalException.new }
Signal.trap('TERM') { raise StopSignalException.new }
Signal.trap('INT') { raise StopSignalException, 'INT' }
Signal.trap('TERM') { raise StopSignalException, 'TERM' }
notifier.run
rescue StopSignalException
notifier.stop
rescue Exception => e
BigBlueButton.logger.error(e.message)
e.backtrace.each do |traceline|
BigBlueButton.logger.error(traceline)
end
end

View File

@ -1,10 +0,0 @@
[Unit]
Description=BigBlueButton recording and playback events worker
ConditionPathExistsGlob=/var/bigbluebutton/recording/status/ended/*.done
[Service]
Type=simple
ExecStart=/usr/local/bigbluebutton/core/scripts/rap-events-worker.rb
WorkingDirectory=/usr/local/bigbluebutton/core/scripts
User=bigbluebutton
Slice=bbb_record_core.slice

View File

@ -1,4 +0,0 @@
[Unit]
Description=BigBlueButton recording & playback processing
Wants=bbb-rap-events-worker.service
StopWhenUnneeded=true

View File

@ -1,10 +0,0 @@
[Unit]
Description=BigBlueButton record & playback processing timer
[Timer]
OnActiveSec=0
OnUnitInactiveSec=30s
Unit=bbb-record-core.target
[Install]
WantedBy=timers.target