Module: RSMP::Proxy::Modules::Versions

Included in:
RSMP::Proxy
Defined in:
lib/rsmp/proxy/modules/versions.rb

Overview

Version negotiation and handling Manages RSMP version handshake between sites and supervisors

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.version_meets_requirement?(version, requirement) ⇒ Boolean

Use Gem class to check version requirement Requirement must be a string like '1.1.0', '>=1.0.3' or '<2.1.4', or list of strings, like ['<=1.4.0','<1.5.0']



184
185
186
# File 'lib/rsmp/proxy/modules/versions.rb', line 184

def self.version_meets_requirement?(version, requirement)
  Gem::Requirement.new(requirement).satisfied_by?(Gem::Version.new(version))
end

Instance Method Details

#advertised_core_versionsObject

Version strings to advertise before a Core version has been selected. Include known legacy spellings so peers using exact string matching can find a common pre-3.3 version.



21
22
23
24
25
26
# File 'lib/rsmp/proxy/modules/versions.rb', line 21

def advertised_core_versions
  versions = core_versions.flat_map { |version| wire_core_version_aliases(version) }
  configured = @site_settings['core_version']
  versions.unshift configured if RSMP::Schema.normalize_core_version(configured)
  versions.uniq
end

#check_core_version(message) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/rsmp/proxy/modules/versions.rb', line 50

def check_core_version(message)
  versions = core_versions
  candidates = message.versions.filter_map do |wire_version|
    normalized = RSMP::Schema.normalize_core_version(wire_version)
    [wire_version, normalized] if normalized && versions.include?(normalized)
  end
  if candidates.any?
    @core_version_string, @core_version = candidates.max_by do |_wire_version, normalized|
      Gem::Version.new(normalized)
    end
  else
    reason = "RSMP versions [#{message.versions.join(', ')}] requested, " \
             "but only [#{versions.join(', ')}] supported."
    dont_acknowledge message, 'Version message rejected', reason, force: true
    raise HandshakeError, reason
  end
end

#component_list_acknowledgedObject



179
# File 'lib/rsmp/proxy/modules/versions.rb', line 179

def component_list_acknowledged; end

#configured_sxlsObject



32
33
34
# File 'lib/rsmp/proxy/modules/versions.rb', line 32

def configured_sxls
  (@site_settings['sxls'] || []).map { |item| item.transform_keys(&:to_s) }
end

#core_3_3?Boolean



28
29
30
# File 'lib/rsmp/proxy/modules/versions.rb', line 28

def core_3_3?
  core_version && version_meets_requirement?(core_version, '>=3.3.0')
end

#core_versionsObject



7
8
9
10
11
12
13
14
15
16
# File 'lib/rsmp/proxy/modules/versions.rb', line 7

def core_versions
  version = @site_settings['core_version']
  if version == 'latest'
    [RSMP::Schema.latest_core_version]
  elsif version.nil? || version == 'all'
    RSMP::Schema.core_versions
  else
    [RSMP::Schema.normalize_core_version(version)].compact
  end
end

#extraneous_version(message) ⇒ Object



70
71
72
# File 'lib/rsmp/proxy/modules/versions.rb', line 70

def extraneous_version(message)
  dont_acknowledge message, 'Received', 'extraneous Version message'
end

#normalized_core_versions(core_versions) ⇒ Object



127
128
129
130
131
132
133
134
135
136
# File 'lib/rsmp/proxy/modules/versions.rb', line 127

def normalized_core_versions(core_versions)
  case core_versions
  when 'latest'
    [RSMP::Schema.latest_core_version]
  when 'all'
    RSMP::Schema.core_versions
  else
    [core_versions].flatten
  end
end

#primary_configured_sxlObject



36
37
38
# File 'lib/rsmp/proxy/modules/versions.rb', line 36

def primary_configured_sxl
  configured_sxls.first
end

#process_version(message) ⇒ Object



68
# File 'lib/rsmp/proxy/modules/versions.rb', line 68

def process_version(message); end

#send_legacy_version_response(site_id, core_versions) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/rsmp/proxy/modules/versions.rb', line 96

def send_legacy_version_response(site_id, core_versions)
  response_versions = normalized_core_versions(core_versions).map do |version|
    normalized = RSMP::Schema.normalize_core_version(version)
    normalized == core_version ? core_version_string : version
  end.uniq
  attributes = version_message_attributes(site_id, response_versions)
  primary = accepted_sxls.first
  attributes['SXL'] = primary['version'].to_s if primary
  send_message Version.new(attributes), validate: false
end

#send_version(site_id, core_versions) ⇒ Object



74
75
76
# File 'lib/rsmp/proxy/modules/versions.rb', line 74

def send_version(site_id, core_versions)
  send_version_message(site_id, core_versions, step: nil)
end

#send_version_message(site_id, core_versions, step:) ⇒ Object



107
108
109
110
111
# File 'lib/rsmp/proxy/modules/versions.rb', line 107

def send_version_message(site_id, core_versions, step:)
  attributes = version_message_attributes(site_id, core_versions)
  attributes.merge!(version_request_attributes) if step == 'Request'
  send_generated_message Version.new(attributes), validate: false
end

#send_version_request(site_id, core_versions) ⇒ Object



78
79
80
# File 'lib/rsmp/proxy/modules/versions.rb', line 78

def send_version_request(site_id, core_versions)
  send_version_message(site_id, core_versions, step: 'Request')
end

#send_version_response(site_id, core_versions) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rsmp/proxy/modules/versions.rb', line 82

def send_version_response(site_id, core_versions)
  if core_3_3?
    send_generated_message Version.new({
                                         'step' => 'Response',
                                         'RSMP' => [{ 'vers' => core_version }],
                                         'supervisorId' => site_id,
                                         'SXLS' => version_response_sxls,
                                         'receiveAlarms' => @site_settings['receive_alarms'] != false
                                       }), validate: false
  else
    send_legacy_version_response(site_id, core_versions)
  end
end

#site_id_items(site_id) ⇒ Object



147
148
149
# File 'lib/rsmp/proxy/modules/versions.rb', line 147

def site_id_items(site_id)
  [site_id].flatten.map { |id| { 'sId' => id } }
end

#sxl_request_itemsObject



40
41
42
43
44
45
46
47
48
# File 'lib/rsmp/proxy/modules/versions.rb', line 40

def sxl_request_items
  configured_sxls.map do |sxl|
    version = RSMP::Schema.sanitize_version(sxl['version'].to_s)
    item = { 'name' => sxl['name'], 'version' => version }
    prefix = RSMP::Schema.sxl_prefix(sxl['name'], version)
    item['prefix'] = prefix if prefix
    item
  end
end

#valid_sxl_response?(accepted) ⇒ Boolean



171
172
173
174
175
# File 'lib/rsmp/proxy/modules/versions.rb', line 171

def valid_sxl_response?(accepted)
  requested = sxl_request_items.find { |item| item['name'] == accepted['name'] }
  RSMP::Schema.strict_version?(accepted['version']) && requested &&
    requested['version'] == accepted['version']
end

#validate_sxl_response!(sxls) ⇒ Object

Raises:



162
163
164
165
166
167
168
169
# File 'lib/rsmp/proxy/modules/versions.rb', line 162

def validate_sxl_response!(sxls)
  invalid = sxls.find { |accepted| !valid_sxl_response?(accepted) }
  return unless invalid

  raise HandshakeError,
        "Invalid SXL version #{invalid['name']} #{invalid['version']}; " \
        'Core 3.3 requires an exact MAJOR.MINOR.PATCH match'
end

#version_acknowledgedObject



177
# File 'lib/rsmp/proxy/modules/versions.rb', line 177

def version_acknowledged; end

#version_items(core_versions) ⇒ Object



123
124
125
# File 'lib/rsmp/proxy/modules/versions.rb', line 123

def version_items(core_versions)
  normalized_core_versions(core_versions).map { |version| { 'vers' => version } }
end

#version_meets_requirement?(version, requirement) ⇒ Boolean



188
189
190
# File 'lib/rsmp/proxy/modules/versions.rb', line 188

def version_meets_requirement?(version, requirement)
  RSMP::Proxy::Modules::Versions.version_meets_requirement?(version, requirement)
end

#version_message_attributes(site_id, core_versions) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/rsmp/proxy/modules/versions.rb', line 113

def version_message_attributes(site_id, core_versions)
  primary = primary_configured_sxl
  attributes = {
    'RSMP' => version_items(core_versions),
    'siteId' => site_id_items(site_id)
  }
  attributes['SXL'] = primary['version'].to_s if primary
  attributes
end

#version_request_attributesObject



151
152
153
154
155
156
# File 'lib/rsmp/proxy/modules/versions.rb', line 151

def version_request_attributes
  {
    'step' => 'Request',
    'SXLS' => sxl_request_items
  }
end

#version_response_sxlsObject



158
159
160
# File 'lib/rsmp/proxy/modules/versions.rb', line 158

def version_response_sxls
  accepted_sxls + rejected_sxls
end

#wire_core_version_aliases(version) ⇒ Object



138
139
140
141
142
143
144
145
# File 'lib/rsmp/proxy/modules/versions.rb', line 138

def wire_core_version_aliases(version)
  normalized = RSMP::Schema.normalize_core_version(version)
  return [version] unless normalized
  return [normalized] if Gem::Version.new(normalized) >= Gem::Version.new('3.3.0')
  return [normalized] unless normalized.end_with?('.0')

  [normalized, normalized.delete_suffix('.0')]
end