cosmos 5.0.3 → 5.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/cosmos +1 -1
- data/lib/cosmos/api/api.rb +1 -25
- data/lib/cosmos/api/cmd_api.rb +6 -6
- data/lib/cosmos/api/config_api.rb +10 -4
- data/lib/cosmos/api/limits_api.rb +1 -1
- data/lib/cosmos/api/settings_api.rb +19 -7
- data/lib/cosmos/api/target_api.rb +2 -2
- data/lib/cosmos/api/tlm_api.rb +8 -8
- data/lib/cosmos/config/config_parser.rb +2 -2
- data/lib/cosmos/config/meta_config_parser.rb +1 -1
- data/lib/cosmos/logs/log_writer.rb +2 -2
- data/lib/cosmos/microservices/decom_microservice.rb +1 -1
- data/lib/cosmos/microservices/interface_microservice.rb +0 -1
- data/lib/cosmos/microservices/microservice.rb +2 -2
- data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
- data/lib/cosmos/models/cvt_model.rb +6 -6
- data/lib/cosmos/models/gem_model.rb +2 -2
- data/lib/cosmos/models/info_model.rb +1 -1
- data/lib/cosmos/models/interface_status_model.rb +1 -1
- data/lib/cosmos/models/metadata_model.rb +42 -216
- data/lib/cosmos/models/metric_model.rb +2 -2
- data/lib/cosmos/models/microservice_model.rb +1 -1
- data/lib/cosmos/models/microservice_status_model.rb +1 -1
- data/lib/cosmos/models/model.rb +16 -16
- data/lib/cosmos/models/note_model.rb +124 -0
- data/lib/cosmos/models/ping_model.rb +2 -1
- data/lib/cosmos/models/plugin_model.rb +1 -1
- data/lib/cosmos/models/process_status_model.rb +1 -1
- data/lib/cosmos/models/scope_model.rb +9 -6
- data/lib/cosmos/models/settings_model.rb +55 -0
- data/lib/cosmos/models/sorted_model.rb +165 -0
- data/lib/cosmos/models/target_model.rb +20 -20
- data/lib/cosmos/models/tool_config_model.rb +38 -0
- data/lib/cosmos/models/tool_model.rb +1 -1
- data/lib/cosmos/models/widget_model.rb +1 -1
- data/lib/cosmos/operators/microservice_operator.rb +2 -1
- data/lib/cosmos/script/calendar.rb +26 -15
- data/lib/cosmos/system/system.rb +2 -1
- data/lib/cosmos/top_level.rb +5 -1
- data/lib/cosmos/topics/autonomic_topic.rb +2 -2
- data/lib/cosmos/topics/calendar_topic.rb +1 -1
- data/lib/cosmos/topics/command_decom_topic.rb +31 -1
- data/lib/cosmos/topics/command_topic.rb +6 -4
- data/lib/cosmos/topics/interface_topic.rb +8 -8
- data/lib/cosmos/topics/limits_event_topic.rb +5 -3
- data/lib/cosmos/topics/notifications_topic.rb +1 -1
- data/lib/cosmos/topics/router_topic.rb +9 -9
- data/lib/cosmos/topics/telemetry_decom_topic.rb +1 -1
- data/lib/cosmos/topics/telemetry_topic.rb +1 -1
- data/lib/cosmos/topics/timeline_topic.rb +1 -1
- data/lib/cosmos/topics/topic.rb +21 -16
- data/lib/cosmos/utilities/logger.rb +3 -3
- data/lib/cosmos/utilities/metric.rb +32 -26
- data/lib/cosmos/utilities/s3.rb +1 -1
- data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
- data/lib/cosmos/utilities/store.rb +1 -0
- data/lib/cosmos/utilities/store_autoload.rb +27 -126
- data/lib/cosmos/version.rb +5 -5
- metadata +7 -4
- data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# This program may also be used under the terms of a commercial or
|
17
|
+
# enterprise edition license of COSMOS if purchased from the
|
18
|
+
# copyright holder
|
19
|
+
|
20
|
+
require 'cosmos/models/model'
|
21
|
+
|
22
|
+
module Cosmos
|
23
|
+
class SettingsModel < Model
|
24
|
+
PRIMARY_KEY = 'cosmos__settings'
|
25
|
+
|
26
|
+
# NOTE: The following three class methods are used by the ModelController
|
27
|
+
# and are reimplemented to enable various Model class methods to work
|
28
|
+
def self.get(name:, scope: nil)
|
29
|
+
super(PRIMARY_KEY, name: name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.names(scope: nil)
|
33
|
+
super(PRIMARY_KEY)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.all(scope: nil)
|
37
|
+
super(PRIMARY_KEY)
|
38
|
+
end
|
39
|
+
# END NOTE
|
40
|
+
|
41
|
+
def initialize(name:, scope: nil, data:)
|
42
|
+
super(PRIMARY_KEY, name: name, scope: scope)
|
43
|
+
@data = data
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Hash] JSON encoding of this model
|
47
|
+
def as_json
|
48
|
+
{
|
49
|
+
'name' => @name,
|
50
|
+
'data' => @data,
|
51
|
+
'updated_at' => @updated_at
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# This program may also be used under the terms of a commercial or
|
17
|
+
# enterprise edition license of COSMOS if purchased from the
|
18
|
+
# copyright holder
|
19
|
+
|
20
|
+
# https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
|
21
|
+
# https://redis.io/docs/manual/data-types/data-types-tutorial/#sorted-sets
|
22
|
+
|
23
|
+
require 'cosmos/models/model'
|
24
|
+
require 'cosmos/topics/calendar_topic'
|
25
|
+
|
26
|
+
module Cosmos
|
27
|
+
# Put these under the Cosmos module so they are easily accessed in the controller as
|
28
|
+
# Cosmos::SortedError vs Cosmos::SortedModel::Error
|
29
|
+
class SortedError < StandardError; end
|
30
|
+
class SortedInputError < SortedError; end
|
31
|
+
class SortedOverlapError < SortedError; end
|
32
|
+
|
33
|
+
class SortedModel < Model
|
34
|
+
SORTED_TYPE = 'sorted'.freeze # To be overriden by base class
|
35
|
+
PRIMARY_KEY = '__SORTED'.freeze # To be overriden by base class
|
36
|
+
|
37
|
+
# MUST be overriden by any subclasses
|
38
|
+
def self.pk(scope)
|
39
|
+
"#{scope}#{PRIMARY_KEY}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String|nil] String of the saved json or nil if start not found
|
43
|
+
def self.get(start:, scope:)
|
44
|
+
result = Store.zrangebyscore(self.pk(scope), start, start)
|
45
|
+
return JSON.parse(result[0]) unless result.empty?
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Array<Hash>] Array up to the limit of the models (as Hash objects) stored under the primary key
|
50
|
+
def self.all(scope:, limit: 100)
|
51
|
+
result = Store.zrevrangebyscore(self.pk(scope), '+inf', '-inf', limit: [0, limit])
|
52
|
+
result.map { |item| JSON.parse(item) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [String|nil] json or nil if metadata empty
|
56
|
+
def self.get_current_value(scope:)
|
57
|
+
start = Time.now.to_i
|
58
|
+
array = Store.zrevrangebyscore(self.pk(scope), start, '-inf', limit: [0, 1])
|
59
|
+
return nil if array.empty?
|
60
|
+
return array[0]
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param start [Integer] Start time to return values (inclusive)
|
64
|
+
# @param stop [Integer] Stop time to return values (inclusive)
|
65
|
+
# @return [Array|nil] Array up to 100 of this model or empty array
|
66
|
+
def self.range(start:, stop:, scope:, limit: 100)
|
67
|
+
if start > stop
|
68
|
+
raise SortedInputError.new "start: #{start} must be before stop: #{stop}"
|
69
|
+
end
|
70
|
+
result = Store.zrangebyscore(self.pk(scope), start, stop, limit: [0, limit])
|
71
|
+
result.map { |item| JSON.parse(item) }
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Integer] count of the members stored under the primary key
|
75
|
+
def self.count(scope:)
|
76
|
+
Store.zcard(self.pk(scope))
|
77
|
+
end
|
78
|
+
|
79
|
+
# Remove member from a sorted set
|
80
|
+
# @return [Integer] count of the members removed, 0 if not found
|
81
|
+
def self.destroy(scope:, start:)
|
82
|
+
Store.zremrangebyscore(self.pk(scope), start, start)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Remove members from min to max of the sorted set.
|
86
|
+
# @return [Integer] count of the members removed
|
87
|
+
def self.range_destroy(scope:, start:, stop:)
|
88
|
+
Store.zremrangebyscore(self.pk(scope), start, stop)
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_reader :start
|
92
|
+
|
93
|
+
# @param [Integer] start - start used to store data
|
94
|
+
# @param [String] scope - Cosmos scope to track event to
|
95
|
+
# @param [Anything] kwargs - Any kwargs to store in the JSON
|
96
|
+
def initialize(start:, scope:, type: SORTED_TYPE, **kwargs)
|
97
|
+
# Name becomes the start in the base class
|
98
|
+
super(self.class.pk(scope), name: start.to_s, scope: scope, **kwargs)
|
99
|
+
@type = type # For the as_json, from_json round trip
|
100
|
+
@start = start
|
101
|
+
end
|
102
|
+
|
103
|
+
# start MUST be a positive integer
|
104
|
+
def validate_start(update: false)
|
105
|
+
unless @start.is_a?(Integer)
|
106
|
+
raise SortedInputError.new "start must be integer: #{@start}"
|
107
|
+
end
|
108
|
+
if @start.to_i < 0
|
109
|
+
raise SortedInputError.new "start must be positive: #{@start}"
|
110
|
+
end
|
111
|
+
if !update and self.class.get(start: @start, scope: @scope)
|
112
|
+
raise SortedOverlapError.new "duplicate, existing data at #{@start}"
|
113
|
+
end
|
114
|
+
@start = @start.to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
# Update the Redis hash at primary_key based on the initial passed start
|
118
|
+
# The member is set to the JSON generated via calling as_json
|
119
|
+
def create(update: false)
|
120
|
+
validate_start(update: update)
|
121
|
+
@updated_at = Time.now.to_nsec_from_epoch
|
122
|
+
Store.zadd(@primary_key, @start, JSON.generate(as_json()))
|
123
|
+
if update
|
124
|
+
notify(kind: 'updated')
|
125
|
+
else
|
126
|
+
notify(kind: 'created')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Update the Redis hash at primary_key
|
131
|
+
def update(start:)
|
132
|
+
@start = start
|
133
|
+
create(update: true)
|
134
|
+
end
|
135
|
+
|
136
|
+
# destroy the activity from the redis database
|
137
|
+
def destroy
|
138
|
+
self.class.destroy(scope: @scope, start: @start)
|
139
|
+
notify(kind: 'deleted')
|
140
|
+
end
|
141
|
+
|
142
|
+
# @return [] update the redis stream / timeline topic that something has changed
|
143
|
+
def notify(kind:, extra: nil)
|
144
|
+
notification = {
|
145
|
+
'data' => JSON.generate(as_json()),
|
146
|
+
'kind' => kind,
|
147
|
+
'type' => 'calendar',
|
148
|
+
}
|
149
|
+
notification['extra'] = extra unless extra.nil?
|
150
|
+
begin
|
151
|
+
CalendarTopic.write_entry(notification, scope: @scope)
|
152
|
+
rescue StandardError => e
|
153
|
+
raise SortedError.new "Failed to write to stream: #{notification}, #{e}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# @return [Hash] JSON encoding of this model
|
158
|
+
def as_json
|
159
|
+
{ **super(),
|
160
|
+
'start' => @start,
|
161
|
+
'type' => SORTED_TYPE,
|
162
|
+
}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -208,7 +208,7 @@ module Cosmos
|
|
208
208
|
tlm_log_cycle_time: tlm_log_cycle_time, tlm_log_cycle_size: tlm_log_cycle_size,
|
209
209
|
tlm_log_retain_time: tlm_log_retain_time,
|
210
210
|
tlm_decom_log_cycle_time: tlm_decom_log_cycle_time, tlm_decom_log_cycle_size: tlm_decom_log_cycle_size,
|
211
|
-
tlm_decom_log_retain_time: tlm_decom_log_retain_time,
|
211
|
+
tlm_decom_log_retain_time: tlm_decom_log_retain_time,
|
212
212
|
reduced_minute_log_retain_time: reduced_minute_log_retain_time,
|
213
213
|
reduced_hour_log_retain_time: reduced_hour_log_retain_time, reduced_day_log_retain_time: reduced_day_log_retain_time,
|
214
214
|
cleanup_poll_time: cleanup_poll_time, needs_dependencies: needs_dependencies,
|
@@ -269,7 +269,7 @@ module Cosmos
|
|
269
269
|
'tlm_decom_log_retain_time' => @tlm_decom_log_retain_time,
|
270
270
|
'reduced_minute_log_retain_time' => @reduced_minute_log_retain_time,
|
271
271
|
'reduced_hour_log_retain_time' => @reduced_hour_log_retain_time,
|
272
|
-
'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
|
272
|
+
'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
|
273
273
|
'cleanup_poll_time' => @cleanup_poll_time,
|
274
274
|
'needs_dependencies' => @needs_dependencies,
|
275
275
|
}
|
@@ -301,7 +301,7 @@ module Cosmos
|
|
301
301
|
when 'CMD_DECOM_LOG_RETAIN_TIME'
|
302
302
|
parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for cmd decom log files in seconds - nil = Forever>")
|
303
303
|
@cmd_decom_log_retain_time = ConfigParser.handle_nil(parameters[0])
|
304
|
-
@cmd_decom_log_retain_time = @cmd_decom_log_retain_time.to_i if @cmd_decom_log_retain_time
|
304
|
+
@cmd_decom_log_retain_time = @cmd_decom_log_retain_time.to_i if @cmd_decom_log_retain_time
|
305
305
|
when 'TLM_LOG_CYCLE_TIME'
|
306
306
|
parser.verify_num_parameters(1, 1, "#{keyword} <Maximum time between files in seconds>")
|
307
307
|
@tlm_log_cycle_time = parameters[0].to_i
|
@@ -311,7 +311,7 @@ module Cosmos
|
|
311
311
|
when 'TLM_LOG_RETAIN_TIME'
|
312
312
|
parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for tlm log files in seconds - nil = Forever>")
|
313
313
|
@tlm_log_retain_time = ConfigParser.handle_nil(parameters[0])
|
314
|
-
@tlm_log_retain_time = @tlm_log_retain_time.to_i if @tlm_log_retain_time
|
314
|
+
@tlm_log_retain_time = @tlm_log_retain_time.to_i if @tlm_log_retain_time
|
315
315
|
when 'TLM_DECOM_LOG_CYCLE_TIME'
|
316
316
|
parser.verify_num_parameters(1, 1, "#{keyword} <Maximum time between files in seconds>")
|
317
317
|
@tlm_decom_log_cycle_time = parameters[0].to_i
|
@@ -333,22 +333,22 @@ module Cosmos
|
|
333
333
|
when 'REDUCED_DAY_LOG_RETAIN_TIME'
|
334
334
|
parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for reduced day log files in seconds - nil = Forever>")
|
335
335
|
@reduced_day_log_retain_time = ConfigParser.handle_nil(parameters[0])
|
336
|
-
@reduced_day_log_retain_time = @reduced_day_log_retain_time.to_i if @reduced_day_log_retain_time
|
336
|
+
@reduced_day_log_retain_time = @reduced_day_log_retain_time.to_i if @reduced_day_log_retain_time
|
337
337
|
when 'LOG_RETAIN_TIME'
|
338
338
|
parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for all log files in seconds - nil = Forever>")
|
339
339
|
log_retain_time = ConfigParser.handle_nil(parameters[0])
|
340
|
-
if log_retain_time
|
340
|
+
if log_retain_time
|
341
341
|
@cmd_log_retain_time = log_retain_time.to_i
|
342
|
-
@cmd_decom_log_retain_time = log_retain_time.to_i
|
342
|
+
@cmd_decom_log_retain_time = log_retain_time.to_i
|
343
343
|
@tlm_log_retain_time = log_retain_time.to_i
|
344
344
|
@tlm_decom_log_retain_time = log_retain_time.to_i
|
345
|
-
end
|
345
|
+
end
|
346
346
|
when 'REDUCED_LOG_RETAIN_TIME'
|
347
347
|
parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for all reduced log files in seconds - nil = Forever>")
|
348
348
|
reduced_log_retain_time = ConfigParser.handle_nil(parameters[0])
|
349
|
-
if reduced_log_retain_time
|
349
|
+
if reduced_log_retain_time
|
350
350
|
@reduced_minute_log_retain_time = reduced_log_retain_time.to_i
|
351
|
-
@reduced_hour_log_retain_time = reduced_log_retain_time.to_i
|
351
|
+
@reduced_hour_log_retain_time = reduced_log_retain_time.to_i
|
352
352
|
@reduced_day_log_retain_time = reduced_log_retain_time.to_i
|
353
353
|
end
|
354
354
|
when 'CLEANUP_POLL_TIME'
|
@@ -380,7 +380,7 @@ module Cosmos
|
|
380
380
|
data = File.read(filename, mode: "rb")
|
381
381
|
begin
|
382
382
|
Cosmos.set_working_dir(File.dirname(filename)) do
|
383
|
-
data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
|
383
|
+
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
|
384
384
|
end
|
385
385
|
rescue => error
|
386
386
|
raise "ERB error parsing: #{filename}: #{error.formatted}"
|
@@ -413,12 +413,12 @@ module Cosmos
|
|
413
413
|
Store.hdel("#{@scope}__limits_groups", group)
|
414
414
|
end
|
415
415
|
self.class.packets(@name, type: :CMD, scope: @scope).each do |packet|
|
416
|
-
|
417
|
-
|
416
|
+
Topic.del("#{@scope}__COMMAND__{#{@name}}__#{packet['packet_name']}")
|
417
|
+
Topic.del("#{@scope}__DECOMCMD__{#{@name}}__#{packet['packet_name']}")
|
418
418
|
end
|
419
419
|
self.class.packets(@name, scope: @scope).each do |packet|
|
420
|
-
|
421
|
-
|
420
|
+
Topic.del("#{@scope}__TELEMETRY__{#{@name}}__#{packet['packet_name']}")
|
421
|
+
Topic.del("#{@scope}__DECOM__{#{@name}}__#{packet['packet_name']}")
|
422
422
|
CvtModel.del(target_name: @name, packet_name: packet['packet_name'], scope: @scope)
|
423
423
|
LimitsEventTopic.delete(@name, packet['packet_name'], scope: @scope)
|
424
424
|
end
|
@@ -455,7 +455,7 @@ module Cosmos
|
|
455
455
|
|
456
456
|
begin
|
457
457
|
Cosmos.set_working_dir(File.dirname(path)) do
|
458
|
-
return ERB.new(File.read(path)).result(b)
|
458
|
+
return ERB.new(File.read(path), trim_mode: "-").result(b)
|
459
459
|
end
|
460
460
|
rescue => error
|
461
461
|
raise "ERB error parsing: #{path}: #{error.formatted}"
|
@@ -585,10 +585,10 @@ module Cosmos
|
|
585
585
|
# No telemetry packets for this target
|
586
586
|
end
|
587
587
|
# It's ok to call initialize_streams with an empty array
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
588
|
+
Topic.initialize_streams(command_topic_list)
|
589
|
+
Topic.initialize_streams(decom_command_topic_list)
|
590
|
+
Topic.initialize_streams(packet_topic_list)
|
591
|
+
Topic.initialize_streams(decom_topic_list)
|
592
592
|
|
593
593
|
unless command_topic_list.empty?
|
594
594
|
# CommandLog Microservice
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# This program may also be used under the terms of a commercial or
|
17
|
+
# enterprise edition license of COSMOS if purchased from the
|
18
|
+
# copyright holder
|
19
|
+
|
20
|
+
module Cosmos
|
21
|
+
class ToolConfigModel
|
22
|
+
def self.list_configs(tool, scope: $cosmos_scope)
|
23
|
+
Store.hkeys("#{scope}__config__#{tool}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.load_config(tool, name, scope: $cosmos_scope)
|
27
|
+
Store.hget("#{scope}__config__#{tool}", name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.save_config(tool, name, data, scope: $cosmos_scope)
|
31
|
+
Store.hset("#{scope}__config__#{tool}", name, data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.delete_config(tool, name, scope: $cosmos_scope)
|
35
|
+
Store.hdel("#{scope}__config__#{tool}", name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -231,7 +231,7 @@ module Cosmos
|
|
231
231
|
|
232
232
|
# Load tool files
|
233
233
|
data = File.read(filename, mode: "rb")
|
234
|
-
data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
|
234
|
+
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
235
235
|
rubys3_client.put_object(bucket: 'tools', content_type: content_type, cache_control: cache_control, key: key, body: data)
|
236
236
|
end
|
237
237
|
end
|
@@ -121,7 +121,7 @@ module Cosmos
|
|
121
121
|
# Load widget file
|
122
122
|
data = File.read(filename, mode: "rb")
|
123
123
|
Cosmos.set_working_dir(File.dirname(filename)) do
|
124
|
-
data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
|
124
|
+
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
125
125
|
end
|
126
126
|
# TODO: support widgets that aren't just a single js file (and its associated map file)
|
127
127
|
rubys3_client.put_object(bucket: 'tools', content_type: 'application/javascript', cache_control: cache_control, key: @s3_key, body: data)
|
@@ -39,7 +39,7 @@ module Cosmos
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def convert_microservice_to_process_definition(microservice_name, microservice_config)
|
42
|
-
process_definition = ["ruby", "plugin_microservice.rb"
|
42
|
+
process_definition = ["ruby", "plugin_microservice.rb"]
|
43
43
|
work_dir = "/cosmos/lib/cosmos/microservices"
|
44
44
|
env = microservice_config["env"].dup
|
45
45
|
if microservice_config["needs_dependencies"]
|
@@ -47,6 +47,7 @@ module Cosmos
|
|
47
47
|
else
|
48
48
|
env['GEM_HOME'] = nil
|
49
49
|
end
|
50
|
+
env['COSMOS_MICROSERVICE_NAME'] = microservice_name
|
50
51
|
container = microservice_config["container"]
|
51
52
|
scope = microservice_name.split("__")[0]
|
52
53
|
return process_definition, work_dir, env, scope, container
|
@@ -18,6 +18,7 @@
|
|
18
18
|
# copyright holder
|
19
19
|
|
20
20
|
require 'cosmos/script/extract'
|
21
|
+
require 'time'
|
21
22
|
|
22
23
|
module Cosmos
|
23
24
|
module Script
|
@@ -25,38 +26,48 @@ module Cosmos
|
|
25
26
|
|
26
27
|
private
|
27
28
|
|
28
|
-
#
|
29
|
+
# Gets the current metadata
|
29
30
|
#
|
30
|
-
# @param target [String] Target to set metadata on
|
31
31
|
# @return The result of the method call.
|
32
|
-
def get_metadata(
|
33
|
-
|
34
|
-
response = $api_server.request('get', endpoint)
|
32
|
+
def get_metadata()
|
33
|
+
response = $api_server.request('get', "/cosmos-api/metadata/latest")
|
35
34
|
return nil if response.nil? || response.code != 200
|
36
35
|
return JSON.parse(response.body)
|
37
36
|
end
|
38
37
|
|
39
|
-
# Sets the metadata
|
38
|
+
# Sets the metadata
|
40
39
|
#
|
41
|
-
# @param target [String] Target to set metadata on
|
42
40
|
# @param metadata [Hash<Symbol, Variable>] A hash of metadata
|
43
|
-
# @param color [String] Events color to show on Calendar tool
|
44
|
-
# @param start
|
41
|
+
# @param color [String] Events color to show on Calendar tool, if nil will be blue
|
42
|
+
# @param start [Time] Metadata time value, if nil will be current time
|
45
43
|
# @return The result of the method call.
|
46
|
-
def set_metadata(
|
44
|
+
def set_metadata(metadata, color: nil, start: nil)
|
47
45
|
color = color.nil? ? '#003784' : color
|
48
|
-
data = {:
|
49
|
-
data[:start] = start unless start.nil?
|
46
|
+
data = { color: color, metadata: metadata }
|
47
|
+
data[:start] = start.iso8601 unless start.nil?
|
50
48
|
response = $api_server.request('post', '/cosmos-api/metadata', data: data, json: true)
|
51
49
|
return nil if response.nil? || response.code != 201
|
52
50
|
return JSON.parse(response.body)
|
53
51
|
end
|
54
52
|
|
55
|
-
#
|
53
|
+
# Updates the metadata
|
56
54
|
#
|
57
|
-
|
58
|
-
|
55
|
+
# @param start [Integer] Metadata time value as integer seconds from epoch
|
56
|
+
# @param metadata [Hash<Symbol, Variable>] A hash of metadata
|
57
|
+
# @param color [String] Events color to show on Calendar tool, if nil will be blue
|
58
|
+
# @return The result of the method call.
|
59
|
+
def update_metadata(start, metadata, color: nil)
|
60
|
+
color = color.nil? ? '#003784' : color
|
61
|
+
data = { :color => color, :metadata => metadata }
|
62
|
+
data[:start] = Time.at(start).iso8601
|
63
|
+
response = $api_server.request('put', "/cosmos-api/metadata/#{start}", data: data, json: true)
|
64
|
+
return nil if response.nil? || response.code != 201
|
65
|
+
return JSON.parse(response.body)
|
59
66
|
end
|
60
67
|
|
68
|
+
# Requests the metadata from the user for a target
|
69
|
+
def input_metadata(*args, **kwargs)
|
70
|
+
raise StandardError "can only be used in Script Runner"
|
71
|
+
end
|
61
72
|
end
|
62
73
|
end
|
data/lib/cosmos/system/system.rb
CHANGED
@@ -26,6 +26,7 @@ require 'cosmos/packets/limits'
|
|
26
26
|
require 'cosmos/system/target'
|
27
27
|
require 'cosmos/utilities/s3'
|
28
28
|
require 'cosmos/utilities/zip'
|
29
|
+
require 'cosmos/models/scope_model'
|
29
30
|
require 'thread'
|
30
31
|
require 'fileutils'
|
31
32
|
|
@@ -54,7 +55,7 @@ module Cosmos
|
|
54
55
|
|
55
56
|
# @return [Symbol] The current limits_set of the system returned from Redis
|
56
57
|
def self.limits_set
|
57
|
-
|
58
|
+
ScopeModel.limits_set(scope: $cosmos_scope)
|
58
59
|
end
|
59
60
|
|
60
61
|
def self.setup_targets(target_names, base_dir, scope:)
|
data/lib/cosmos/top_level.rb
CHANGED
@@ -37,9 +37,13 @@ class HazardousError < StandardError
|
|
37
37
|
attr_accessor :cmd_name
|
38
38
|
attr_accessor :cmd_params
|
39
39
|
attr_accessor :hazardous_description
|
40
|
+
attr_accessor :formatted # formatted command for use in resending original
|
40
41
|
|
41
42
|
def to_s
|
42
|
-
"#{target_name} #{cmd_name} with #{cmd_params} is Hazardous
|
43
|
+
string = "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous"
|
44
|
+
string << "due to '#{hazardous_description}'" if hazardous_description
|
45
|
+
# Pass along the original formatted command so it can be resent
|
46
|
+
string << ".\n#{formatted}"
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
@@ -26,7 +26,7 @@ module Cosmos
|
|
26
26
|
# Notify to the topic
|
27
27
|
#
|
28
28
|
# ```json
|
29
|
-
# {
|
29
|
+
# {
|
30
30
|
# "kind" => "created",
|
31
31
|
# "type" => "trigger",
|
32
32
|
# "data" => {
|
@@ -46,7 +46,7 @@ module Cosmos
|
|
46
46
|
# }
|
47
47
|
# ```
|
48
48
|
def self.write_notification(notification, scope:)
|
49
|
-
|
49
|
+
Topic.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -40,7 +40,37 @@ module Cosmos
|
|
40
40
|
json_hash[item.name + "__U"] = packet.read_item(item, :WITH_UNITS) if item.units
|
41
41
|
end
|
42
42
|
msg_hash['json_data'] = JSON.generate(json_hash.as_json)
|
43
|
-
|
43
|
+
Topic.write_topic(topic, msg_hash)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get_cmd_item(target_name, packet_name, param_name, type: :WITH_UNITS, scope: $cosmos_scope)
|
47
|
+
msg_id, msg_hash = Topic.get_newest_message("#{scope}__DECOMCMD__{#{target_name}}__#{packet_name}")
|
48
|
+
if msg_id
|
49
|
+
# TODO: We now have these reserved items directly on command packets
|
50
|
+
# Do we still calculate from msg_hash['time'] or use the times directly?
|
51
|
+
#
|
52
|
+
# if param_name == 'RECEIVED_TIMESECONDS' || param_name == 'PACKET_TIMESECONDS'
|
53
|
+
# Time.from_nsec_from_epoch(msg_hash['time'].to_i).to_f
|
54
|
+
# elsif param_name == 'RECEIVED_TIMEFORMATTED' || param_name == 'PACKET_TIMEFORMATTED'
|
55
|
+
# Time.from_nsec_from_epoch(msg_hash['time'].to_i).formatted
|
56
|
+
if param_name == 'RECEIVED_COUNT'
|
57
|
+
msg_hash['received_count'].to_i
|
58
|
+
else
|
59
|
+
json = msg_hash['json_data']
|
60
|
+
hash = JSON.parse(json)
|
61
|
+
# Start from the most complex down to the basic raw value
|
62
|
+
value = hash["#{param_name}__U"]
|
63
|
+
return value if value && type == :WITH_UNITS
|
64
|
+
|
65
|
+
value = hash["#{param_name}__F"]
|
66
|
+
return value if value && (type == :WITH_UNITS || type == :FORMATTED)
|
67
|
+
|
68
|
+
value = hash["#{param_name}__C"]
|
69
|
+
return value if value && (type == :WITH_UNITS || type == :FORMATTED || type == :CONVERTED)
|
70
|
+
|
71
|
+
return hash[param_name]
|
72
|
+
end
|
73
|
+
end
|
44
74
|
end
|
45
75
|
end
|
46
76
|
end
|
@@ -31,17 +31,17 @@ module Cosmos
|
|
31
31
|
received_count: packet.received_count,
|
32
32
|
stored: packet.stored,
|
33
33
|
buffer: packet.buffer(false) }
|
34
|
-
|
34
|
+
Topic.write_topic(topic, msg_hash)
|
35
35
|
end
|
36
36
|
|
37
37
|
# @param command [Hash] Command hash structure read to be written to a topic
|
38
38
|
def self.send_command(command, scope:)
|
39
39
|
ack_topic = "{#{scope}__ACKCMD}TARGET__#{command['target_name']}"
|
40
|
-
|
40
|
+
Topic.update_topic_offsets([ack_topic])
|
41
41
|
# Save the existing cmd_params Hash and JSON generate before writing to the topic
|
42
42
|
cmd_params = command['cmd_params']
|
43
43
|
command['cmd_params'] = JSON.generate(command['cmd_params'].as_json)
|
44
|
-
cmd_id =
|
44
|
+
cmd_id = Topic.write_topic("{#{scope}__CMD}TARGET__#{command['target_name']}", command, '*', 100)
|
45
45
|
# TODO: This timeout is fine for most but can we get the write_timeout from the interface here?
|
46
46
|
time = Time.now
|
47
47
|
while (Time.now - time) < COMMAND_ACK_TIMEOUT_S
|
@@ -66,7 +66,7 @@ module Cosmos
|
|
66
66
|
###########################################################################
|
67
67
|
|
68
68
|
def self.raise_hazardous_error(msg_hash, target_name, cmd_name, cmd_params)
|
69
|
-
_, description,
|
69
|
+
_, description, formatted = msg_hash["result"].split("\n")
|
70
70
|
# Create and populate a new HazardousError and raise it up
|
71
71
|
# The _cmd method in script/commands.rb rescues this and calls prompt_for_hazardous
|
72
72
|
error = HazardousError.new
|
@@ -74,6 +74,8 @@ module Cosmos
|
|
74
74
|
error.cmd_name = cmd_name
|
75
75
|
error.cmd_params = cmd_params
|
76
76
|
error.hazardous_description = description
|
77
|
+
error.formatted = formatted
|
78
|
+
|
77
79
|
# No Logger.info because the error is already logged by the Logger.info "Ack Received ...
|
78
80
|
raise error
|
79
81
|
end
|