cosmos 5.0.2.pre.beta2 → 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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +1 -1
  3. data/data/config/microservice.yaml +47 -35
  4. data/data/config/plugins.yaml +3 -150
  5. data/data/config/target.yaml +70 -0
  6. data/data/config/tool.yaml +37 -31
  7. data/lib/cosmos/api/api.rb +1 -25
  8. data/lib/cosmos/api/cmd_api.rb +17 -6
  9. data/lib/cosmos/api/config_api.rb +10 -4
  10. data/lib/cosmos/api/limits_api.rb +1 -1
  11. data/lib/cosmos/api/settings_api.rb +19 -7
  12. data/lib/cosmos/api/target_api.rb +2 -2
  13. data/lib/cosmos/api/tlm_api.rb +69 -41
  14. data/lib/cosmos/config/config_parser.rb +19 -22
  15. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  16. data/lib/cosmos/conversions/generic_conversion.rb +2 -2
  17. data/lib/cosmos/conversions/polynomial_conversion.rb +5 -8
  18. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +26 -9
  19. data/lib/cosmos/io/json_drb.rb +5 -1
  20. data/lib/cosmos/logs/log_writer.rb +2 -2
  21. data/lib/cosmos/microservices/cleanup_microservice.rb +28 -29
  22. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  23. data/lib/cosmos/microservices/interface_microservice.rb +0 -1
  24. data/lib/cosmos/microservices/microservice.rb +3 -3
  25. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  26. data/lib/cosmos/models/cvt_model.rb +6 -6
  27. data/lib/cosmos/models/gem_model.rb +3 -3
  28. data/lib/cosmos/models/info_model.rb +1 -1
  29. data/lib/cosmos/models/interface_status_model.rb +1 -1
  30. data/lib/cosmos/models/metadata_model.rb +42 -216
  31. data/lib/cosmos/models/metric_model.rb +2 -2
  32. data/lib/cosmos/models/microservice_model.rb +1 -1
  33. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  34. data/lib/cosmos/models/model.rb +16 -16
  35. data/lib/cosmos/models/note_model.rb +124 -0
  36. data/lib/cosmos/models/ping_model.rb +2 -1
  37. data/lib/cosmos/models/plugin_model.rb +1 -1
  38. data/lib/cosmos/models/process_status_model.rb +1 -1
  39. data/lib/cosmos/models/scope_model.rb +9 -26
  40. data/lib/cosmos/models/settings_model.rb +55 -0
  41. data/lib/cosmos/models/sorted_model.rb +165 -0
  42. data/lib/cosmos/models/target_model.rb +120 -13
  43. data/lib/cosmos/models/tool_config_model.rb +38 -0
  44. data/lib/cosmos/models/tool_model.rb +1 -1
  45. data/lib/cosmos/models/widget_model.rb +1 -1
  46. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  47. data/lib/cosmos/packets/packet.rb +23 -0
  48. data/lib/cosmos/packets/packet_config.rb +2 -2
  49. data/lib/cosmos/packets/packet_item.rb +57 -0
  50. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  51. data/lib/cosmos/packets/parsers/packet_item_parser.rb +1 -1
  52. data/lib/cosmos/packets/parsers/packet_parser.rb +1 -1
  53. data/lib/cosmos/packets/parsers/xtce_parser.rb +1 -1
  54. data/lib/cosmos/packets/structure_item.rb +10 -1
  55. data/lib/cosmos/script/api_shared.rb +30 -25
  56. data/lib/cosmos/script/calendar.rb +26 -15
  57. data/lib/cosmos/script/commands.rb +5 -7
  58. data/lib/cosmos/script/script.rb +19 -39
  59. data/lib/cosmos/script/storage.rb +92 -105
  60. data/lib/cosmos/system/system.rb +2 -1
  61. data/lib/cosmos/tools/table_manager/table_item.rb +1 -1
  62. data/lib/cosmos/top_level.rb +5 -1
  63. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  64. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  65. data/lib/cosmos/topics/command_decom_topic.rb +35 -1
  66. data/lib/cosmos/topics/command_topic.rb +6 -4
  67. data/lib/cosmos/topics/interface_topic.rb +8 -8
  68. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  69. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  70. data/lib/cosmos/topics/router_topic.rb +9 -9
  71. data/lib/cosmos/topics/telemetry_decom_topic.rb +5 -1
  72. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  73. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  74. data/lib/cosmos/topics/topic.rb +23 -8
  75. data/lib/cosmos/utilities/logger.rb +4 -3
  76. data/lib/cosmos/utilities/metric.rb +32 -26
  77. data/lib/cosmos/utilities/s3.rb +61 -0
  78. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  79. data/lib/cosmos/utilities/store.rb +1 -0
  80. data/lib/cosmos/utilities/store_autoload.rb +25 -134
  81. data/lib/cosmos/version.rb +6 -5
  82. data/templates/plugin-template/plugin.gemspec +0 -2
  83. metadata +9 -6
  84. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -0,0 +1,124 @@
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
+
22
+ require 'cosmos/models/sorted_model'
23
+
24
+ module Cosmos
25
+ class NoteModel < SortedModel
26
+ NOTE_TYPE = 'note'.freeze
27
+ PRIMARY_KEY = '__NOTE'.freeze
28
+
29
+ def self.pk(scope)
30
+ "#{scope}#{PRIMARY_KEY}"
31
+ end
32
+
33
+ attr_reader :stop, :color, :description, :type
34
+
35
+ # @param [String] scope - Cosmos scope to track event to
36
+ # @param [Integer] start - start of the event in seconds from Epoch
37
+ # @param [Integer] stop - stop of the event in seconds from Epoch
38
+ # @param [String] color - The event color
39
+ # @param [String] description - What the event is about
40
+ def initialize(
41
+ scope:,
42
+ start:,
43
+ stop:,
44
+ color: nil,
45
+ description:,
46
+ type: NOTE_TYPE,
47
+ updated_at: 0
48
+ )
49
+ super(start: start, scope: scope, updated_at: updated_at)
50
+ @start = start
51
+ @stop = stop
52
+ @color = color
53
+ @description = description
54
+ @type = type # For the as_json, from_json round trip
55
+ end
56
+
57
+ # Validates the instance variables: @start, @stop, @color, @description
58
+ def validate(update: false)
59
+ validate_start(update: update)
60
+ validate_stop()
61
+ validate_color()
62
+ end
63
+
64
+ def validate_stop()
65
+ unless @stop.is_a?(Integer)
66
+ raise SortedInputError.new "stop must be integer: #{@stop}"
67
+ end
68
+ if @stop.to_i < @start
69
+ raise SortedInputError.new "stop: #{@stop} must be >= start: #{@start}"
70
+ end
71
+ @stop = @stop.to_i
72
+ end
73
+
74
+ def validate_color()
75
+ if @color.nil?
76
+ @color = '#%06x' % (rand * 0xffffff)
77
+ end
78
+ unless @color =~ /(#*)([0-9,a-f,A-f]{6})/
79
+ raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
80
+ end
81
+ @color = "##{@color}" unless @color.start_with?('#')
82
+ end
83
+
84
+ # Update the Redis hash at primary_key based on the initial passed start
85
+ # The member is set to the JSON generated via calling as_json
86
+ def create(update: false)
87
+ validate(update: update)
88
+ @updated_at = Time.now.to_nsec_from_epoch
89
+ Store.zadd(@primary_key, @start, JSON.generate(as_json()))
90
+ if update
91
+ notify(kind: 'updated')
92
+ else
93
+ notify(kind: 'created')
94
+ end
95
+ end
96
+
97
+ # Update the Redis hash at primary_key
98
+ def update(start:, stop:, color:, description:)
99
+ @start = start
100
+ @stop = stop
101
+ @color = color
102
+ @description = description
103
+ create(update: true)
104
+ end
105
+
106
+ # @return [Hash] generated from the NoteModel
107
+ def as_json
108
+ return {
109
+ 'scope' => @scope,
110
+ 'start' => @start,
111
+ 'stop' => @stop,
112
+ 'color' => @color,
113
+ 'description' => @description,
114
+ 'type' => NOTE_TYPE,
115
+ 'updated_at' => @updated_at,
116
+ }
117
+ end
118
+
119
+ # @return [String] string view of NoteModel
120
+ def to_s
121
+ return "<NoteModel s: #{@start}, x: #{@stop}, c: #{@color}, d: #{@description}>"
122
+ end
123
+ end
124
+ end
@@ -24,7 +24,8 @@ module Cosmos
24
24
  # @return String ['UP' or 'DOWN']
25
25
  def self.get()
26
26
  response = Store.ping()
27
- if response
27
+ response2 = EphemeralStore.ping()
28
+ if response and response2
28
29
  return 'UP'
29
30
  else
30
31
  return 'DOWN'
@@ -125,7 +125,7 @@ module Cosmos
125
125
  gem_file_path = Cosmos::GemModel.get(temp_dir, name)
126
126
 
127
127
  # Actually install the gem now (slow)
128
- Cosmos::GemModel.install(gem_file_path)
128
+ Cosmos::GemModel.install(gem_file_path, scope: scope)
129
129
 
130
130
  # Extract gem contents
131
131
  gem_path = File.join(temp_dir, "gem")
@@ -21,7 +21,7 @@ require 'cosmos/models/model'
21
21
 
22
22
  module Cosmos
23
23
  # Stores the status about an process.
24
- class ProcessStatusModel < Model
24
+ class ProcessStatusModel < EphemeralModel
25
25
  PRIMARY_KEY = 'cosmos_process_status'
26
26
 
27
27
  attr_accessor :state
@@ -20,6 +20,7 @@
20
20
  require 'cosmos/version'
21
21
  require 'cosmos/models/model'
22
22
  require 'cosmos/models/microservice_model'
23
+ require 'cosmos/models/settings_model'
23
24
 
24
25
  module Cosmos
25
26
  class ScopeModel < Model
@@ -51,24 +52,6 @@ module Cosmos
51
52
  def deploy(gem_path, variables)
52
53
  seed_database()
53
54
 
54
- # Cleanup Microservice
55
- microservice_name = "#{@scope}__CLEANUP__S3"
56
- microservice = MicroserviceModel.new(
57
- name: microservice_name,
58
- cmd: ["ruby", "cleanup_microservice.rb", microservice_name],
59
- work_dir: '/cosmos/lib/cosmos/microservices',
60
- options: [
61
- ["SIZE", "20_000_000_000"], # Max Size to keep in S3
62
- ["DELAY", "300"], # Delay between size checks
63
- ["BUCKET", "logs"], # Bucket to monitor
64
- ["PREFIX", @scope + "/"], # Path into bucket to monitor
65
- ],
66
- scope: @scope
67
- )
68
- microservice.create
69
- microservice.deploy(gem_path, variables)
70
- Logger.info "Configured microservice #{microservice_name}"
71
-
72
55
  # COSMOS Log Microservice
73
56
  microservice_name = "#{@scope}__COSMOS__LOG"
74
57
  microservice = MicroserviceModel.new(
@@ -105,7 +88,7 @@ module Cosmos
105
88
  Logger.info "Configured microservice #{microservice_name}"
106
89
 
107
90
  # UNKNOWN CommandLog Microservice
108
- Store.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
91
+ Topic.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
109
92
  microservice_name = "#{@scope}__COMMANDLOG__UNKNOWN"
110
93
  microservice = MicroserviceModel.new(
111
94
  name: microservice_name,
@@ -125,7 +108,7 @@ module Cosmos
125
108
  Logger.info "Configured microservice #{microservice_name}"
126
109
 
127
110
  # UNKNOWN PacketLog Microservice
128
- Store.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
111
+ Topic.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
129
112
  microservice_name = "#{@scope}__PACKETLOG__UNKNOWN"
130
113
  microservice = MicroserviceModel.new(
131
114
  name: microservice_name,
@@ -146,8 +129,6 @@ module Cosmos
146
129
  end
147
130
 
148
131
  def undeploy
149
- model = MicroserviceModel.get_model(name: "#{@scope}__CLEANUP__S3", scope: @scope)
150
- model.destroy if model
151
132
  model = MicroserviceModel.get_model(name: "#{@scope}__COSMOS__LOG", scope: @scope)
152
133
  model.destroy if model
153
134
  model = MicroserviceModel.get_model(name: "#{@scope}__NOTIFICATION__LOG", scope: @scope)
@@ -159,10 +140,12 @@ module Cosmos
159
140
  end
160
141
 
161
142
  def seed_database
162
- # Set default values for items in the db that should be set
163
- # Use the "nx" (not-exists) variant of redis calls here to not overwrite things the user has already set
164
- Cosmos::Store.hsetnx('cosmos__settings', 'source_url', 'https://github.com/BallAerospace/COSMOS')
165
- Cosmos::Store.hsetnx('cosmos__settings', 'version', ENV['COSMOS_VERSION'] || COSMOS_VERSION)
143
+ setting = SettingsModel.get(name: 'source_url')
144
+ SettingsModel.set({ name: 'source_url', data: 'https://github.com/BallAerospace/COSMOS' }, scope: @scope) unless setting
145
+ end
146
+
147
+ def self.limits_set(scope:)
148
+ Store.hget("#{scope}__cosmos_system", 'limits_set').intern
166
149
  end
167
150
  end
168
151
  end
@@ -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