cosmos 5.0.2.pre.beta2 → 5.0.4

Sign up to get free protection for your applications and to get access to all the features.
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