lib/openc3/microservices/timeline_microservice.rb in openc3-5.17.1 vs lib/openc3/microservices/timeline_microservice.rb in openc3-5.18.0
- old
+ new
@@ -22,10 +22,11 @@
require 'openc3/utilities/authentication'
require 'openc3/microservices/microservice'
require 'openc3/models/activity_model'
require 'openc3/models/timeline_model'
+require 'openc3/models/tool_config_model'
require 'openc3/topics/timeline_topic'
require 'openc3/script'
module OpenC3
@@ -80,49 +81,70 @@
else
@logger.error "Unknown kind passed to microservice #{@timeline_name}: #{activity.as_json(:allow_nan => true)}"
end
end
+ def get_exec_setting()
+ json = ToolConfigModel.load_config('calendar-settings', 'default', scope: @scope)
+ if json
+ settings = JSON.parse(json)
+ return settings['execEnabled']
+ else
+ # Default is execute
+ return true
+ end
+ end
+
def run_command(activity)
@logger.info "#{@timeline_name} run_command > #{activity.as_json(:allow_nan => true)}"
begin
- username = activity.data['username']
- token = get_token(username)
- raise "No token available for username: #{username}" unless token
- cmd_no_hazardous_check(activity.data['command'], scope: @scope, token: token)
- activity.commit(status: 'completed', fulfillment: true)
+ if get_exec_setting()
+ username = activity.data['username']
+ token = get_token(username)
+ raise "No token available for username: #{username}" unless token
+ cmd_no_hazardous_check(activity.data['command'], scope: @scope, token: token)
+ activity.commit(status: 'completed', fulfillment: true)
+ else
+ activity.commit(status: 'disabled', message: 'Execution is disabled')
+ @logger.warn "#{@timeline_name} run_command disabled > #{activity.as_json(:allow_nan => true)}"
+ end
rescue StandardError => e
activity.commit(status: 'failed', message: e.message)
- @logger.error "#{@timeline_name} run_cmd failed > #{activity.as_json(:allow_nan => true)}, #{e.formatted}"
+ @logger.error "#{@timeline_name} run_command failed > #{activity.as_json(:allow_nan => true)}, #{e.formatted}"
end
end
def run_script(activity)
@logger.info "#{@timeline_name} run_script > #{activity.as_json(:allow_nan => true)}"
begin
- username = activity.data['username']
- token = get_token(username)
- raise "No token available for username: #{username}" unless token
- request = Net::HTTP::Post.new(
- "/script-api/scripts/#{activity.data['script']}/run?scope=#{@scope}",
- 'Content-Type' => 'application/json',
- 'Authorization' => token
- )
- request.body = JSON.generate({
- 'scope' => @scope,
- 'environment' => activity.data['environment'],
- 'timeline' => @timeline_name,
- 'id' => activity.start
- })
- hostname = ENV['OPENC3_SCRIPT_HOSTNAME'] || 'openc3-cosmos-script-runner-api'
- response = Net::HTTP.new(hostname, 2902).request(request)
- raise "failed to call #{hostname}, for script: #{activity.data['script']}, response code: #{response.code}" if response.code != '200'
+ if get_exec_setting()
+ username = activity.data['username']
+ token = get_token(username)
+ raise "No token available for username: #{username}" unless token
+ request = Net::HTTP::Post.new(
+ "/script-api/scripts/#{activity.data['script']}/run?scope=#{@scope}",
+ 'Content-Type' => 'application/json',
+ 'Authorization' => token
+ )
+ request.body = JSON.generate({
+ 'scope' => @scope,
+ 'environment' => activity.data['environment'],
+ 'timeline' => @timeline_name,
+ 'id' => activity.start
+ })
+ hostname = ENV['OPENC3_SCRIPT_HOSTNAME'] || 'openc3-cosmos-script-runner-api'
+ response = Net::HTTP.new(hostname, 2902).request(request)
+ raise "failed to call #{hostname}, for script: #{activity.data['script']}, response code: #{response.code}" if response.code != '200'
- activity.commit(status: 'completed', message: "#{activity.data['script']} => #{response.body}", fulfillment: true)
+ activity.commit(status: 'completed', message: "#{activity.data['script']} => #{response.body}", fulfillment: true)
+ else
+ activity.commit(status: 'disabled', message: 'Execution is disabled')
+ @logger.warn "#{@timeline_name} run_script disabled > #{activity.as_json(:allow_nan => true)}"
+ end
rescue StandardError => e
activity.commit(status: 'failed', message: e.message)
- @logger.error "#{@timeline_name} run_script failed > #{activity.as_json(:allow_nan => true).to_s}, #{e.message}"
+ @logger.error "#{@timeline_name} run_script failed > #{activity.as_json(:allow_nan => true)}, #{e.message}"
end
end
def clear_expired(activity)
begin
@@ -210,11 +232,11 @@
end
def run
@logger.info "#{@timeline_name} timeline manager running"
loop do
- start = Time.now.to_i
+ start = Time.now.to_f
@schedule.activities.each do |activity|
start_difference = activity.start - start
if start_difference <= 0 && @schedule.not_queued?(activity.start)
@logger.debug "#{@timeline_name} #{@scope} current start: #{start}, vs #{activity.start}, #{start_difference}"
activity.add_event(status: 'queued')
@@ -228,22 +250,22 @@
break if @cancel_thread
sleep(1)
break if @cancel_thread
end
- @logger.info "#{@timeline_name} timeine manager exiting"
+ @logger.info "#{@timeline_name} timeline manager exiting"
end
# Add task to remove events older than 7 days
def add_expire_activity
- now = Time.now.to_i
- @expire = now + 3_000
+ now = Time.now.to_f
+ @expire = now + 3540 # Needs to be less than 3600 which is the hour we store in memory
activity = ActivityModel.new(
name: @timeline_name,
scope: @scope,
start: 0,
- stop: (now - 86_400 * 7),
+ stop: (now - (86_400 * 7)),
kind: 'expire',
data: {}
)
@queue << activity
return activity
@@ -281,39 +303,39 @@
class TimelineMicroservice < Microservice
def initialize(name)
super(name)
@timeline_name = name.split('__')[2]
@schedule = Schedule.new(@timeline_name)
- @manager = TimelineManager.new(name: @timeline_name, logger: @logger, scope: scope, schedule: @schedule)
+ @manager = TimelineManager.new(name: @timeline_name, logger: @logger, scope: @scope, schedule: @schedule)
@manager_thread = nil
@read_topic = true
end
def run
- @logger.info "#{@name} timeine running"
+ @logger.info "#{@name} timeline running"
@manager_thread = Thread.new { @manager.run }
loop do
current_activities = ActivityModel.activities(name: @timeline_name, scope: @scope)
@schedule.update(current_activities)
break if @cancel_thread
block_for_updates()
break if @cancel_thread
end
- @logger.info "#{@name} timeine exitting"
+ @logger.info "#{@name} timeline exiting"
end
def topic_lookup_functions
{
'timeline' => {
- 'created' => :timeline_nop,
+ 'created' => :timeline_noop,
'refresh' => :schedule_refresh,
- 'updated' => :timeline_nop,
- 'deleted' => :timeline_nop
+ 'updated' => :timeline_noop,
+ 'deleted' => :timeline_noop
},
'activity' => {
- 'event' => :timeline_nop,
+ 'event' => :timeline_noop,
'created' => :create_activity_from_event,
'updated' => :schedule_refresh,
'deleted' => :remove_activity_from_event
}
}
@@ -333,11 +355,11 @@
@logger.error "#{@timeline_name} failed to read topics #{@topics}\n#{e.formatted}"
end
end
end
- def timeline_nop(data)
+ def timeline_noop(data)
@logger.debug "#{@name} timeline web socket event: #{data}"
end
def schedule_refresh(data)
@logger.debug "#{@name} timeline web socket schedule refresh: #{data}"
@@ -345,30 +367,33 @@
end
# Add the activity to the schedule. We don't need to hold the job in memory
# if it is longer than an hour away. A refresh task will update that.
def create_activity_from_event(data)
- diff = data['start'] - Time.now.to_i
- return unless (2..3600).include? diff
+ diff = data['start'] - Time.now.to_f
+ return if diff < 0 or diff > 3600
activity = ActivityModel.from_json(data, name: @timeline_name, scope: @scope)
@schedule.add_activity(activity)
end
# Remove the activity from the schedule. We don't need to remove the activity
# if it is longer than an hour away. It will be removed from the data.
def remove_activity_from_event(data)
- diff = data['start'] - Time.now.to_i
- return unless (2..3600).include? diff
+ diff = data['start'] - Time.now.to_f
+ return if diff < 0 or diff > 3600
activity = ActivityModel.from_json(data, name: @timeline_name, scope: @scope)
@schedule.remove_activity(activity)
end
def shutdown
- @read_topic = false
@manager.shutdown
- super
+ # super also sets @cancel_thread = true but we want to set it first
+ # so when we set @read_topic = false the run loop stops
+ @cancel_thread = true
+ @read_topic = false
+ super()
end
end
end
OpenC3::TimelineMicroservice.run if __FILE__ == $0