Class: Mt::Wall::Plan

Inherits:
Object
  • Object
show all
Defined in:
lib/mt/wall/plan.rb

Overview

The diff between a desired and a current DesiredState: an ordered list of operations. A Plan is both printable (for ‘mt-wall plan`) and executable (handed to a Transport by `apply`).

IDENTITY MATCHING — ‘(tag, ordinal)`: filter/nat rows are matched desired<->current by their content-only `mt-wall:<stable-hash>` identity tag (in `comment`), never by the opaque device `.id`. The tag is content-only, so two rows CAN legitimately share a tag (different position, same content). Matching therefore keys on the pair `(tag, ordinal)` where `ordinal` is the 0-based occurrence index of that tag within its (path, chain), assigned in document order on each side. The Compiler collapses EXACT duplicates (same content + chain) into ONE row, so a surviving ordinal > 0 is a deliberate, semantically distinct repeat. Match outcomes per `(tag, ordinal)`:

* present in desired, absent in current => :create;
* a current mt-wall-tagged `(tag, ordinal)` absent from desired => :delete;
* matched pair, differing normalized payload => :update;
* matched pair in the wrong position => :move.

The ‘.id` is carried in the payload of :update/:delete/:move only so the transport can address the existing row (PATCH/DELETE/move target it by id).

ORDERING IS FIRST-CLASS because mt-wall owns the whole filter/nat table and rule order is semantically load-bearing (RouterOS evaluates top-down). Operations therefore include explicit placement; the place-before anchor is resolved against the ‘(tag, ordinal)` of the row it must precede:

* :create carries `position` (place-before anchor) so a new rule lands
  at the right index;
* :move reorders an existing row to sit before `position`.

APPLY-ORDER INVARIANTS (the Plan is emitted already sorted so a transport can execute it verbatim; see Reconciler):

1. create/insert the stateful preamble, accept rules and the
   management-protection rule BEFORE any tightening;
2. create-before-delete (never delete an old rule before its
   replacement exists);
3. install default-DROP policy rows LAST.

Defined Under Namespace

Classes: Differ, Operation

Constant Summary collapse

ID_KEY =

RouterOS row handle. Lives ONLY on fetched current rows; carried into the payload of :update/:delete/:move so the transport can address the row.

:".id"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(operations = []) ⇒ Plan

Returns a new instance of Plan.



65
66
67
# File 'lib/mt/wall/plan.rb', line 65

def initialize(operations = [])
  @operations = operations
end

Instance Attribute Details

#operationsObject (readonly)

Returns the value of attribute operations.



63
64
65
# File 'lib/mt/wall/plan.rb', line 63

def operations
  @operations
end

#pathObject (readonly)

RouterOS resource path (managed table)



53
54
55
56
57
# File 'lib/mt/wall/plan.rb', line 53

Operation = Data.define(:action, :path, :payload, :position) do
  def initialize(action:, path:, payload: {}, position: nil)
    super
  end
end

#positionObject (readonly)

place-before anchor: the ‘(tag, ordinal)` pair (or `.id`) of the row this one must precede; nil = append at the table’s tail



53
54
55
56
57
# File 'lib/mt/wall/plan.rb', line 53

Operation = Data.define(:action, :path, :payload, :position) do
  def initialize(action:, path:, payload: {}, position: nil)
    super
  end
end

Class Method Details

.diff(desired:, current:) ⇒ Plan

Build a Plan by diffing two DesiredState instances. Matches rows by the ‘(tag, ordinal)` key (filter/nat) or the natural key (address-list), and normalizes both sides (string-coerced values, `dynamic` rows excluded) before comparing. Emits operations already sorted per the apply-order invariants above.

Parameters:

Returns:



82
83
84
# File 'lib/mt/wall/plan.rb', line 82

def self.diff(desired:, current:)
  Differ.new(desired: desired, current: current).call
end

Instance Method Details

#action=(value) ⇒ Object

:create, :update, :delete or :move



53
54
55
56
57
# File 'lib/mt/wall/plan.rb', line 53

Operation = Data.define(:action, :path, :payload, :position) do
  def initialize(action:, path:, payload: {}, position: nil)
    super
  end
end

#empty?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/mt/wall/plan.rb', line 69

def empty?
  @operations.empty?
end

#payload=(value) ⇒ Object

the resource attributes; for :update/:delete/:move it also carries the existing row’s ‘.id`



53
54
55
56
57
# File 'lib/mt/wall/plan.rb', line 53

Operation = Data.define(:action, :path, :payload, :position) do
  def initialize(action:, path:, payload: {}, position: nil)
    super
  end
end