lib/arborist/node.rb in arborist-0.0.1.pre20160106113421 vs lib/arborist/node.rb in arborist-0.0.1.pre20160128152542

- old
+ new

@@ -27,15 +27,11 @@ ## # The key for the thread local that is used to track instances as they're # loaded. LOADED_INSTANCE_KEY = :loaded_node_instances - ## - # The glob pattern to use for searching for node - NODE_FILE_PATTERN = '**/*.rb' - ## # The struct for the 'ack' operational property ACK = Struct.new( 'ArboristNodeACK', :message, :via, :sender, :time ) ## @@ -55,23 +51,30 @@ state_machine( :status, initial: :unknown ) do state :unknown, :up, :down, - :acked + :acked, + :disabled event :update do - transition any - [:acked] => :acked, if: :ack_set? - transition any - [:up] => :up, if: :last_contact_successful? - transition any - [:down, :acked] => :down, unless: :last_contact_successful? + transition [:down, :unknown, :acked] => :up, if: :last_contact_successful? + transition [:up, :unknown] => :down, unless: :last_contact_successful? + transition :down => :acked, if: :ack_set? + transition [:unknown, :up] => :disabled, if: :ack_set? + transition :disabled => :unknown, unless: :ack_set? end after_transition any => :acked, do: :on_ack after_transition :acked => :up, do: :on_ack_cleared after_transition :down => :up, do: :on_node_up after_transition [:unknown, :up] => :down, do: :on_node_down + after_transition [:unknown, :up] => :disabled, do: :on_node_disabled + after_transition :disabled => :unknown, do: :on_node_enabled + after_transition any => any, do: :log_transition + after_transition do: :add_status_to_update_delta end ### Return a curried Proc for the ::create method for the specified +type+. @@ -98,10 +101,12 @@ ### Record a new loaded instance if the Thread-local variable is set up to track ### them. def self::add_loaded_instance( new_instance ) instances = Thread.current[ LOADED_INSTANCE_KEY ] or return + self.log.debug "Adding new instance %p to loaded instances %p" % + [ new_instance, instances ] instances << new_instance end ### Inheritance hook -- add a DSL declarative function for the given +subclass+. @@ -135,28 +140,13 @@ ensure Thread.current[ LOADED_INSTANCE_KEY ] = nil end - ### Return an iterator for all the node files in the specified +directory+. - def self::each_in( directory ) - path = Pathname( directory ) - paths = if path.directory? - Pathname.glob( directory + NODE_FILE_PATTERN ) - else - [ path ] - end - - return paths.flat_map do |file| - file_url = "file://%s" % [ file.expand_path ] - nodes = self.load( file ) - self.log.debug "Loaded nodes %p..." % [ nodes ] - nodes.each do |node| - node.source = file_url - end - nodes - end + ### Return an iterator for all the nodes supplied by the specified +loader+. + def self::each_in( loader ) + return loader.nodes end ### Create a new Node with the specified +identifier+, which must be unique to the ### loaded tree. @@ -321,12 +311,15 @@ def update( new_properties ) new_properties = stringify_keys( new_properties ) self.log.debug "Updated: %p" % [ new_properties ] self.last_contacted = Time.now - self.error = new_properties.delete( 'error' ) - self.ack = new_properties.delete( 'ack' ) if new_properties.key?( 'ack' ) + if new_properties.key?( 'ack' ) + self.ack = new_properties.delete( 'ack' ) + else + self.error = new_properties.delete( 'error' ) + end self.properties.merge!( new_properties, &self.method(:merge_and_record_delta) ) compact_hash( self.properties ) # Super to the state machine event method @@ -502,10 +495,12 @@ case self.status when 'up', 'down' return "%s as of %s" % [ self.status.upcase, self.last_contacted ] when 'acked' return "ACKed by %s %s" % [ self.ack.sender, self.ack.time.as_delta ] + when 'disabled' + return "disabled by %s %s" % [ self.ack.sender, self.ack.time.as_delta ] else return "in an unknown state" end end @@ -605,21 +600,25 @@ protected ######### ### Ack the node with the specified +ack_data+, which should contain def ack=( ack_data ) - self.log.debug "ACKed with data: %p" % [ ack_data ] + if ack_data + self.log.info "Node %s ACKed with data: %p" % [ self.identifier, ack_data ] + ack_data['time'] ||= Time.now + ack_values = ack_data.values_at( *Arborist::Node::ACK.members.map(&:to_s) ) + new_ack = Arborist::Node::ACK.new( *ack_values ) - ack_data['time'] ||= Time.now - ack_values = ack_data.values_at( *Arborist::Node::ACK.members.map(&:to_s) ) - new_ack = Arborist::Node::ACK.new( *ack_values ) + if missing = ACK_REQUIRED_PROPERTIES.find {|prop| new_ack[prop].nil? } + raise "Missing required ACK attribute %s" % [ missing ] + end - if missing = ACK_REQUIRED_PROPERTIES.find {|prop| new_ack[prop].nil? } - raise "Missing required ACK attribute %s" % [ missing ] + @ack = new_ack + else + self.log.info "Node %s ACK cleared explicitly" % [ self.identifier ] + @ack = nil end - - @ack = new_ack end ### State machine guard predicate -- Returns +true+ if the node has an ACK status set. def ack_set? @@ -640,33 +639,54 @@ # # :section: State Callbacks # + ### Log every status transition + def log_transition( transition ) + self.log.debug "Transitioned %s from %s to %s" % + [ self.identifier, transition.from, transition.to ] + end + + ### Callback for when an acknowledgement is set. def on_ack( transition ) self.log.warn "ACKed: %s" % [ self.status_description ] self.pending_update_events << Arborist::Event.create( 'node_acked', self.fetch_values, self.ack.to_h ) end ### Callback for when an acknowledgement is cleared. def on_ack_cleared( transition ) + self.error = nil self.log.warn "ACK cleared for %s" % [ self.identifier ] end ### Callback for when a node goes from down to up def on_node_up( transition ) + self.error = nil self.log.warn "%s is %s" % [ self.identifier, self.status_description ] end ### Callback for when a node goes from up to down def on_node_down( transition ) self.log.error "%s is %s" % [ self.identifier, self.status_description ] self.update_delta[ 'error' ] = [ nil, self.error ] + end + + + ### Callback for when a node goes from up to disabled + def on_node_disabled( transition ) + self.log.warn "%s is %s" % [ self.identifier, self.status_description ] + end + + + ### Callback for when a node goes from disabled to unknown + def on_node_enabled( transition ) + self.log.warn "%s is %s" % [ self.identifier, self.status_description ] end ### Add the transition from one state to another to the data used to build ### deltas for the #update event.