Ravn::Tactical::

Change class

Compute pack change queue item class.

Instances of this class are changes that should be applied to nodes in a kit. It specifies an Operation (the name of a Ravn::Tactical::Operation), a Node (Ravn::Tactical::Node), and a (potentially nil) set of options to pass to the Operation.

Attributes

config RW

The Hash of configuration options the Operation uses when it runs.

created_at RW

The timestamp when the Change was created

id RW

The unique ID of the change

node RW

The Ravn::Tactical::Node this change applies to

node_id RW

The ID of the node the Change will be run on

type RW

The name of the Ravn::Tactical::Operation that will be run for this change

Public Class Methods

create_for_all_nodes( type, include_local: false, **config, &block )

Create a new Change for all undeleted Nodes in the current Kit of the specified type and config.

    # File lib/ravn/tactical/change.rb
118 def self::create_for_all_nodes( type, include_local: false, **config, &block )
119     nodes = Ravn::Tactical::Node.undeleted
120     nodes = nodes.except_local unless include_local
121 
122     return self.create_for_nodes( type, nodes, **config, &block )
123 end
create_for_nodes( type, nodes, **config, &block )

Create a new Change for each of the given nodes of the specified type and config.

    # File lib/ravn/tactical/change.rb
106 def self::create_for_nodes( type, nodes, **config, &block )
107     # :TODO: Make the default `nodes` all registered nodes
108     self.db.transaction do
109         return Array( nodes ).flatten.map do |node|
110             self.create( type:, node:, config:, &block )
111         end
112     end
113 end
new( ... )

Set up some instance variables on creation.

    # File lib/ravn/tactical/change.rb
131 def initialize( ... )
132     super
133 
134     @config = nil
135 end

Public Instance Methods

by_node()

Group the dataset by node.

   # File lib/ravn/tactical/change.rb
69 def by_node
70     return self.group( :node )
71 end
config=( new_config )

Set the config options for the Operation.

    # File lib/ravn/tactical/change.rb
184 def config=( new_config )
185     new_config = symbolify_keys( new_config )
186     return if new_config == self.config
187 
188     @config = deep_freeze( new_config )
189 
190     self.will_change_column( :config )
191     self[:config] = Yajl::Encoder.encode( @config )
192 end
earliest_changes()

Return a naked dataset that contains just the IDs of the latest change per node.

   # File lib/ravn/tactical/change.rb
82 def earliest_changes
83     return self.select {
84         first_value( :id ).
85             over( partition: :node_id, order: :created_at ).
86             as( :id )
87     }.distinct.naked
88 end
earliest_for_each_node()

Return a dataset that yields only the earliest Change for each node.

   # File lib/ravn/tactical/change.rb
92 def earliest_for_each_node
93     return self.with( :earliest_changes, self.earliest_changes ).
94         where( id: self.db[:earliest_changes].select(:id) )
95 end
for_nodes( *nodes )

Limit the dataset to entries for the given node.

   # File lib/ravn/tactical/change.rb
62 def for_nodes( *nodes )
63     return self.where( node: nodes.flatten )
64 end
get_config( key_path )

Get a single config value specified by key_path.

    # File lib/ravn/tactical/change.rb
221 def get_config( key_path )
222     keys = split_key_path( key_path )
223     return self.config.dig( *keys )
224 end
inspect_details()

Ravn::Inspection API – Return the details of the change in a human-readable format suitable for debugging.

    # File lib/ravn/tactical/change.rb
247 def inspect_details
248     return "[%d] %s operation on %s (%s), created: %s" % [
249         self.id || -1,
250         self.type,
251         self.node ? self.node.device_id : "an unspecified node",
252         self.config&.keys&.join(', '),
253         self.created_at ? self.created_at.rfc2822 : '-',
254     ]
255 end
older_first()

Returns the dataset sorted by time of creation.

   # File lib/ravn/tactical/change.rb
75 def older_first
76     return self.order( :created_at )
77 end
operation()

Return a Ravn::Tactical::Operation that knows how to affect the Change.

    # File lib/ravn/tactical/change.rb
164 def operation
165     return Ravn::Tactical::Operation.from_change( self )
166 end
set_config( key_path, value )

Set a single config value, marking the object as modified if the config was changed. This method will auto-vivify Hashes for any missing branches, but can also traverse Arrays (ala dig).

    # File lib/ravn/tactical/change.rb
198 def set_config( key_path, value )
199     keys = split_key_path( key_path )
200     self.log.debug "Setting config %p: %p" % [ keys, value ]
201 
202     changed_config = deep_copy( self.config )
203     leaf_branch = keys[ 0..-2 ].inject( changed_config ) do |section, idx|
204         if (subsection = section[idx])
205             self.log.debug "  found existing subsection %p" % [ idx ]
206             subsection
207         else
208             self.log.debug "  adding subsection %p" % [ idx ]
209             section[ idx ] = {}
210         end
211     end
212 
213     self.log.debug "  leaf branch is: %p" % [ leaf_branch ]
214     leaf_branch[ keys.last ] = value
215 
216     self.config = changed_config
217 end
validate()

Sequel::Model API – Validate the object before saving.

    # File lib/ravn/tactical/change.rb
228 def validate
229     super
230 
231     self.validate_node
232 end
validate_node()

Ensure the associated node is present and valid.

    # File lib/ravn/tactical/change.rb
236 def validate_node
237     if self.node
238         self.validate_associated_object( self.model.association_reflection(:node), self.node )
239     else
240         self.errors.add( :node, "is not set" )
241     end
242 end