rsmp 0.51.0 → 0.51.1
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/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/documentation/configuration.md +8 -0
- data/lib/rsmp/convert/export/json_schema/index.rb +4 -5
- data/lib/rsmp/message.rb +2 -3
- data/lib/rsmp/node/site/site.rb +2 -1
- data/lib/rsmp/node/supervisor/modules/configuration.rb +24 -11
- data/lib/rsmp/proxy/modules/versions.rb +61 -11
- data/lib/rsmp/proxy/proxy.rb +3 -3
- data/lib/rsmp/proxy/site/sxl_selection.rb +7 -2
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +2 -1
- data/lib/rsmp/schema/version.rb +27 -0
- data/lib/rsmp/schema.rb +1 -0
- data/lib/rsmp/tlc/modules/system.rb +2 -2
- data/lib/rsmp/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b41aa5a1cbba1d8803f5c579b06b91472e0f9aed82db0c5ed70b14881f1faef4
|
|
4
|
+
data.tar.gz: 7d487e564d6626c0fc306e328d45dcff7576cbac442f31971784de1b0be7bdd6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37b1f43e03c6ef6c4b41c95d8b3f2b0c394f656c7285bde99b14d4d4c8bfabdd6c0230fccd0861115d02eb6a4d04a97ecc012a2824efd3b8fdf5f48b6a0a53fb
|
|
7
|
+
data.tar.gz: fae75a302258af53de4308eba92dc39d3eb4ee3d00cdceead6c12e7ccc9773a03085bb2df9df21e6c2dcb658532044caa1f83a5fcd564e74c0e31560b53abf38
|
data/CHANGELOG.md
CHANGED
|
@@ -711,3 +711,11 @@ Initial release.
|
|
|
711
711
|
- publish typed events for expected peer and connection failures
|
|
712
712
|
- preserve unexpected exceptions and backtraces through structured Async task trees
|
|
713
713
|
- add explicit bang variants for callers that prefer exception-based handling
|
|
714
|
+
|
|
715
|
+
## 0.51.0
|
|
716
|
+
- fix Async listener and reconnect lifecycle cleanup
|
|
717
|
+
- prevent teardown races when acknowledgement and proxy tasks stop concurrently
|
|
718
|
+
- fix RuboCop violations
|
|
719
|
+
|
|
720
|
+
## 0.51.1
|
|
721
|
+
- cleanup semantic versioning handling of version strings and use canonical schema versions internally
|
data/Gemfile.lock
CHANGED
|
@@ -16,6 +16,14 @@ RSMP uses option classes to handle configuration for sites and supervisors. Thes
|
|
|
16
16
|
|
|
17
17
|
Each class applies defaults and validates the configuration against a JSON Schema located in `lib/rsmp/options/schemas/`.
|
|
18
18
|
|
|
19
|
+
## Version strings
|
|
20
|
+
|
|
21
|
+
Core 3.3.0 and later use exact `MAJOR.MINOR.PATCH` version strings for both Core and SXL negotiation.
|
|
22
|
+
|
|
23
|
+
Earlier Core versions retain their historical wire formats for compatibility. In particular, the Core 3.2 release may be written as either `3.2` or `3.2.0`. The library resolves both to the `3.2.0` schema internally, but a supervisor responding to a legacy Version message uses the exact Core and SXL strings sent by the site. Ambiguous versions such as `3.1` are rejected because they do not identify one of the supported 3.1.x releases.
|
|
24
|
+
|
|
25
|
+
Legacy SXL strings such as `1.1` and `1.2` remain supported for connections using a Core version before 3.3.0. Their three-part equivalents are used only for internal schema lookup.
|
|
26
|
+
|
|
19
27
|
## Loading a configuration file
|
|
20
28
|
|
|
21
29
|
Use the option classes to load a YAML configuration file:
|
|
@@ -4,13 +4,12 @@ module RSMP
|
|
|
4
4
|
# Converts SXL definitions to JSON Schema files.
|
|
5
5
|
module JSONSchema
|
|
6
6
|
def self.output_sxl_index(out, sxl)
|
|
7
|
+
legacy_types = legacy_types?(sxl)
|
|
7
8
|
out['sxl_index.json'] = output_json({
|
|
8
9
|
'meta' => sxl[:meta],
|
|
9
|
-
'statuses' => index_items(sxl[:statuses],
|
|
10
|
-
|
|
11
|
-
'
|
|
12
|
-
legacy_types: legacy_types?(sxl)),
|
|
13
|
-
'alarms' => index_items(sxl[:alarms], legacy_types: legacy_types?(sxl))
|
|
10
|
+
'statuses' => index_items(sxl[:statuses], legacy_types: legacy_types),
|
|
11
|
+
'commands' => index_items(sxl[:commands], legacy_types: legacy_types),
|
|
12
|
+
'alarms' => index_items(sxl[:alarms], legacy_types: legacy_types)
|
|
14
13
|
})
|
|
15
14
|
end
|
|
16
15
|
|
data/lib/rsmp/message.rb
CHANGED
|
@@ -202,10 +202,9 @@ module RSMP
|
|
|
202
202
|
|
|
203
203
|
# Represents a malformed message with invalid attributes.
|
|
204
204
|
class Malformed < Message
|
|
205
|
-
# rubocop:disable-next Lint/MissingSuper
|
|
206
205
|
def initialize(attributes = {})
|
|
207
|
-
|
|
208
|
-
@attributes
|
|
206
|
+
super({})
|
|
207
|
+
@attributes.clear
|
|
209
208
|
@invalid_attributes = attributes
|
|
210
209
|
end
|
|
211
210
|
end
|
data/lib/rsmp/node/site/site.rb
CHANGED
|
@@ -81,8 +81,9 @@ module RSMP
|
|
|
81
81
|
def check_core_versions
|
|
82
82
|
version = @site_settings['core_version']
|
|
83
83
|
return unless version
|
|
84
|
+
return if %w[all latest].include? version
|
|
84
85
|
|
|
85
|
-
return if RSMP::Schema.
|
|
86
|
+
return if RSMP::Schema.normalize_core_version version
|
|
86
87
|
|
|
87
88
|
error_str = "Unknown core version: #{version}"
|
|
88
89
|
raise RSMP::ConfigurationError, error_str
|
|
@@ -35,23 +35,36 @@ module RSMP
|
|
|
35
35
|
sites.each do |site_id, settings|
|
|
36
36
|
raise RSMP::ConfigurationError, "Configuration for site '#{site_id}' is empty" unless settings
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
check_configured_sxls site_id, settings['sxls']
|
|
39
|
+
check_configured_core_version site_id, settings['core_version']
|
|
40
|
+
rescue RSMP::Schema::UnknownSchemaError => e
|
|
41
|
+
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': #{e}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if name.to_s == 'core'
|
|
44
|
-
raise RSMP::ConfigurationError,
|
|
45
|
-
"Configuration error for site '#{site_id}': SXL name cannot be core"
|
|
46
|
-
end
|
|
45
|
+
def check_configured_sxls(site_id, sxls)
|
|
46
|
+
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': No SXLs specified" unless sxls
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
sxls.each do |sxl|
|
|
49
|
+
name = sxl['name']
|
|
50
|
+
if name.to_s == 'core'
|
|
51
|
+
raise RSMP::ConfigurationError,
|
|
52
|
+
"Configuration error for site '#{site_id}': SXL name cannot be core"
|
|
49
53
|
end
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
|
|
55
|
+
RSMP::Schema.find_schema! name, sxl['version'], lenient: true
|
|
52
56
|
end
|
|
53
57
|
end
|
|
54
58
|
|
|
59
|
+
def check_configured_core_version(site_id, core_version)
|
|
60
|
+
return unless core_version
|
|
61
|
+
return if %w[all latest].include? core_version
|
|
62
|
+
return if RSMP::Schema.normalize_core_version core_version
|
|
63
|
+
|
|
64
|
+
raise RSMP::ConfigurationError,
|
|
65
|
+
"Configuration error for site '#{site_id}': Unknown core version: #{core_version}"
|
|
66
|
+
end
|
|
67
|
+
|
|
55
68
|
def site_id_to_site_setting(site_id)
|
|
56
69
|
base = @supervisor_settings['default'] || {}
|
|
57
70
|
|
|
@@ -8,13 +8,23 @@ module RSMP
|
|
|
8
8
|
version = @site_settings['core_version']
|
|
9
9
|
if version == 'latest'
|
|
10
10
|
[RSMP::Schema.latest_core_version]
|
|
11
|
-
elsif version
|
|
12
|
-
[version]
|
|
13
|
-
else
|
|
11
|
+
elsif version.nil? || version == 'all'
|
|
14
12
|
RSMP::Schema.core_versions
|
|
13
|
+
else
|
|
14
|
+
[RSMP::Schema.normalize_core_version(version)].compact
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# Version strings to advertise before a Core version has been selected.
|
|
19
|
+
# Include known legacy spellings so peers using exact string matching
|
|
20
|
+
# can find a common pre-3.3 version.
|
|
21
|
+
def advertised_core_versions
|
|
22
|
+
versions = core_versions.flat_map { |version| wire_core_version_aliases(version) }
|
|
23
|
+
configured = @site_settings['core_version']
|
|
24
|
+
versions.unshift configured if RSMP::Schema.normalize_core_version(configured)
|
|
25
|
+
versions.uniq
|
|
26
|
+
end
|
|
27
|
+
|
|
18
28
|
def core_3_3?
|
|
19
29
|
core_version && version_meets_requirement?(core_version, '>=3.3.0')
|
|
20
30
|
end
|
|
@@ -29,8 +39,9 @@ module RSMP
|
|
|
29
39
|
|
|
30
40
|
def sxl_request_items
|
|
31
41
|
configured_sxls.map do |sxl|
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
version = RSMP::Schema.sanitize_version(sxl['version'].to_s)
|
|
43
|
+
item = { 'name' => sxl['name'], 'version' => version }
|
|
44
|
+
prefix = RSMP::Schema.sxl_prefix(sxl['name'], version)
|
|
34
45
|
item['prefix'] = prefix if prefix
|
|
35
46
|
item
|
|
36
47
|
end
|
|
@@ -38,10 +49,14 @@ module RSMP
|
|
|
38
49
|
|
|
39
50
|
def check_core_version(message)
|
|
40
51
|
versions = core_versions
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
candidates = message.versions.filter_map do |wire_version|
|
|
53
|
+
normalized = RSMP::Schema.normalize_core_version(wire_version)
|
|
54
|
+
[wire_version, normalized] if normalized && versions.include?(normalized)
|
|
55
|
+
end
|
|
43
56
|
if candidates.any?
|
|
44
|
-
@core_version = candidates.max_by
|
|
57
|
+
@core_version_string, @core_version = candidates.max_by do |_wire_version, normalized|
|
|
58
|
+
Gem::Version.new(normalized)
|
|
59
|
+
end
|
|
45
60
|
else
|
|
46
61
|
reason = "RSMP versions [#{message.versions.join(', ')}] requested, " \
|
|
47
62
|
"but only [#{versions.join(', ')}] supported."
|
|
@@ -74,10 +89,21 @@ module RSMP
|
|
|
74
89
|
'receiveAlarms' => @site_settings['receive_alarms'] != false
|
|
75
90
|
}), validate: false
|
|
76
91
|
else
|
|
77
|
-
|
|
92
|
+
send_legacy_version_response(site_id, core_versions)
|
|
78
93
|
end
|
|
79
94
|
end
|
|
80
95
|
|
|
96
|
+
def send_legacy_version_response(site_id, core_versions)
|
|
97
|
+
response_versions = normalized_core_versions(core_versions).map do |version|
|
|
98
|
+
normalized = RSMP::Schema.normalize_core_version(version)
|
|
99
|
+
normalized == core_version ? core_version_string : version
|
|
100
|
+
end.uniq
|
|
101
|
+
attributes = version_message_attributes(site_id, response_versions)
|
|
102
|
+
primary = accepted_sxls.first
|
|
103
|
+
attributes['SXL'] = primary['version'].to_s if primary
|
|
104
|
+
send_message Version.new(attributes), validate: false
|
|
105
|
+
end
|
|
106
|
+
|
|
81
107
|
def send_version_message(site_id, core_versions, step:)
|
|
82
108
|
attributes = version_message_attributes(site_id, core_versions)
|
|
83
109
|
attributes.merge!(version_request_attributes) if step == 'Request'
|
|
@@ -109,6 +135,15 @@ module RSMP
|
|
|
109
135
|
end
|
|
110
136
|
end
|
|
111
137
|
|
|
138
|
+
def wire_core_version_aliases(version)
|
|
139
|
+
normalized = RSMP::Schema.normalize_core_version(version)
|
|
140
|
+
return [version] unless normalized
|
|
141
|
+
return [normalized] if Gem::Version.new(normalized) >= Gem::Version.new('3.3.0')
|
|
142
|
+
return [normalized] unless normalized.end_with?('.0')
|
|
143
|
+
|
|
144
|
+
[normalized, normalized.delete_suffix('.0')]
|
|
145
|
+
end
|
|
146
|
+
|
|
112
147
|
def site_id_items(site_id)
|
|
113
148
|
[site_id].flatten.map { |id| { 'sId' => id } }
|
|
114
149
|
end
|
|
@@ -124,13 +159,28 @@ module RSMP
|
|
|
124
159
|
accepted_sxls + rejected_sxls
|
|
125
160
|
end
|
|
126
161
|
|
|
162
|
+
def validate_sxl_response!(sxls)
|
|
163
|
+
invalid = sxls.find { |accepted| !valid_sxl_response?(accepted) }
|
|
164
|
+
return unless invalid
|
|
165
|
+
|
|
166
|
+
raise HandshakeError,
|
|
167
|
+
"Invalid SXL version #{invalid['name']} #{invalid['version']}; " \
|
|
168
|
+
'Core 3.3 requires an exact MAJOR.MINOR.PATCH match'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def valid_sxl_response?(accepted)
|
|
172
|
+
requested = sxl_request_items.find { |item| item['name'] == accepted['name'] }
|
|
173
|
+
RSMP::Schema.strict_version?(accepted['version']) && requested &&
|
|
174
|
+
requested['version'] == accepted['version']
|
|
175
|
+
end
|
|
176
|
+
|
|
127
177
|
def version_acknowledged; end
|
|
128
178
|
|
|
129
179
|
def component_list_acknowledged; end
|
|
130
180
|
|
|
131
181
|
# Use Gem class to check version requirement
|
|
132
|
-
# Requirement must be a string like '1.1', '>=1.0.3' or '<2.1.4',
|
|
133
|
-
# or list of strings, like ['<=1.4','<1.5']
|
|
182
|
+
# Requirement must be a string like '1.1.0', '>=1.0.3' or '<2.1.4',
|
|
183
|
+
# or list of strings, like ['<=1.4.0','<1.5.0']
|
|
134
184
|
def self.version_meets_requirement?(version, requirement)
|
|
135
185
|
Gem::Requirement.new(requirement).satisfied_by?(Gem::Version.new(version))
|
|
136
186
|
end
|
data/lib/rsmp/proxy/proxy.rb
CHANGED
|
@@ -19,7 +19,7 @@ module RSMP
|
|
|
19
19
|
include Modules::Lifecycle
|
|
20
20
|
|
|
21
21
|
attr_reader :state, :archive, :connection_info, :sxls, :accepted_sxls, :rejected_sxls,
|
|
22
|
-
:collector, :ip, :port, :node, :core_version, :sxl_interfaces,
|
|
22
|
+
:collector, :ip, :port, :node, :core_version, :core_version_string, :sxl_interfaces,
|
|
23
23
|
:site_settings, :session_id
|
|
24
24
|
|
|
25
25
|
def initialize(options)
|
|
@@ -190,8 +190,8 @@ module RSMP
|
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
# Use Gem class to check version requirement
|
|
193
|
-
# Requirement must be a string like '1.1', '>=1.0.3' or '<2.1.4',
|
|
194
|
-
# or list of strings, like ['<=1.4','<1.5']
|
|
193
|
+
# Requirement must be a string like '1.1.0', '>=1.0.3' or '<2.1.4',
|
|
194
|
+
# or list of strings, like ['<=1.4.0','<1.5.0']
|
|
195
195
|
def self.version_meets_requirement?(version, requirement)
|
|
196
196
|
Modules::Versions.version_meets_requirement?(version, requirement)
|
|
197
197
|
end
|
|
@@ -34,8 +34,13 @@ module RSMP
|
|
|
34
34
|
configured = configured_sxls.find { |item| item['name'] == requested['name'] }
|
|
35
35
|
return rejected_sxl(requested, 1, 'SXL not supported') unless configured
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
unless RSMP::Schema.strict_version?(requested['version'])
|
|
38
|
+
return rejected_sxl(requested, 2, 'Core 3.3 requires SXL versions to use MAJOR.MINOR.PATCH')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
configured_version = RSMP::Schema.sanitize_version(configured['version'].to_s)
|
|
42
|
+
if configured_version == requested['version']
|
|
43
|
+
RSMP::Schema.find_schema! requested['name'], requested['version']
|
|
39
44
|
requested.slice('name', 'version', 'prefix')
|
|
40
45
|
else
|
|
41
46
|
rejected_sxl(requested, 2, "Supervisor only supports #{configured['version']}")
|
|
@@ -63,7 +63,7 @@ module RSMP
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def start_handshake
|
|
66
|
-
send_version_request @site_settings['site_id'],
|
|
66
|
+
send_version_request @site_settings['site_id'], advertised_core_versions
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def close(...)
|
|
@@ -213,6 +213,7 @@ module RSMP
|
|
|
213
213
|
def check_sxl_version(message)
|
|
214
214
|
if core_3_3?
|
|
215
215
|
@rejected_sxls, @accepted_sxls = message.sxls.partition { |item| item['rejected'] }
|
|
216
|
+
validate_sxl_response! @accepted_sxls
|
|
216
217
|
@receive_alarms = message.attributes.fetch('receiveAlarms', true)
|
|
217
218
|
else
|
|
218
219
|
primary = primary_configured_sxl
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
# Provides JSON Schema validation for RSMP messages across core and SXL versions.
|
|
3
|
+
module Schema
|
|
4
|
+
STRICT_VERSION_PATTERN = /\A(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\z/
|
|
5
|
+
LEGACY_SHORT_VERSION_PATTERN = /\A(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\z/
|
|
6
|
+
|
|
7
|
+
# Return the schema version for a supported Core version string.
|
|
8
|
+
# Before Core 3.3, some releases used a two-part version on the wire.
|
|
9
|
+
# Only accept such a version when adding .0 identifies a known legacy
|
|
10
|
+
# schema unambiguously. Core 3.3 and later require all three parts.
|
|
11
|
+
def self.normalize_core_version(version)
|
|
12
|
+
return unless version.is_a?(String)
|
|
13
|
+
return version if core_versions.include? version
|
|
14
|
+
return unless LEGACY_SHORT_VERSION_PATTERN.match? version
|
|
15
|
+
|
|
16
|
+
normalized = "#{version}.0"
|
|
17
|
+
return unless core_versions.include? normalized
|
|
18
|
+
return unless Gem::Version.new(normalized) < Gem::Version.new('3.3.0')
|
|
19
|
+
|
|
20
|
+
normalized
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.strict_version?(version)
|
|
24
|
+
version.is_a?(String) && STRICT_VERSION_PATTERN.match?(version)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/rsmp/schema.rb
CHANGED
|
@@ -64,7 +64,7 @@ module RSMP
|
|
|
64
64
|
|
|
65
65
|
# S0091 - Operator logged in/out OP-panel
|
|
66
66
|
def handle_s0091(_status_code, status_name = nil, options = {})
|
|
67
|
-
if RSMP::Proxy.version_meets_requirement?(options[:sxl_version], '>=1.1')
|
|
67
|
+
if RSMP::Proxy.version_meets_requirement?(options[:sxl_version], '>=1.1.0')
|
|
68
68
|
case status_name
|
|
69
69
|
when 'user'
|
|
70
70
|
TrafficControllerSite.make_status 0
|
|
@@ -81,7 +81,7 @@ module RSMP
|
|
|
81
81
|
|
|
82
82
|
# S0092 - Operator logged in/out web-interface
|
|
83
83
|
def handle_s0092(_status_code, status_name = nil, options = {})
|
|
84
|
-
if RSMP::Proxy.version_meets_requirement?(options[:sxl_version], '>=1.1')
|
|
84
|
+
if RSMP::Proxy.version_meets_requirement?(options[:sxl_version], '>=1.1.0')
|
|
85
85
|
case status_name
|
|
86
86
|
when 'user'
|
|
87
87
|
TrafficControllerSite.make_status 0
|
data/lib/rsmp/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rsmp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.51.
|
|
4
|
+
version: 0.51.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emil Tin
|
|
@@ -253,6 +253,7 @@ files:
|
|
|
253
253
|
- lib/rsmp/schema/core_sxl_resolution.rb
|
|
254
254
|
- lib/rsmp/schema/message_resolution.rb
|
|
255
255
|
- lib/rsmp/schema/validation.rb
|
|
256
|
+
- lib/rsmp/schema/version.rb
|
|
256
257
|
- lib/rsmp/schema_error.rb
|
|
257
258
|
- lib/rsmp/status_list.rb
|
|
258
259
|
- lib/rsmp/sxl/interface.rb
|