Skip to content

Commit

Permalink
crowbar: Create event hook infrastructure
Browse files Browse the repository at this point in the history
There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then to dispatch the notifications to hooks listen
listening to them that will decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - we have two events that are generated and notified: node_changed
   (when a node is saved and some specific attributes have been changed)
   and proposal_applied (when a proposal is successfully applied).

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.

We could add many more events but for the time being, we only add the
events that we know are useful.
  • Loading branch information
vuntz committed Jan 4, 2017
1 parent c8f9a22 commit 83ad9a3
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
22 changes: 21 additions & 1 deletion crowbar_framework/app/models/node_object.rb
Expand Up @@ -16,6 +16,7 @@
#

require "chef/mixin/deep_merge"
require "set"
require "timeout"
require "open3"

Expand Down Expand Up @@ -259,6 +260,7 @@ def initialize(node)
end
# deep clone of @role.default_attributes, used when saving node
@attrs_last_saved = @role.default_attributes.deep_dup
@attributes_changed_for_event = Set.new
@node = node
end

Expand Down Expand Up @@ -289,7 +291,10 @@ def availability_zone
def availability_zone=(value)
@node["crowbar_wall"] ||= {}
@node["crowbar_wall"]["openstack"] ||= {}
@node["crowbar_wall"]["openstack"]["availability_zone"] = value
if @node["crowbar_wall"]["openstack"]["availability_zone"] != value
@node["crowbar_wall"]["openstack"]["availability_zone"] = value
@attributes_changed_for_event.add("availability_zone")
end
end

def intended_role
Expand Down Expand Up @@ -479,6 +484,7 @@ def validate_public_name(value, unique_check = true)
def update_public_name(value)
unless value.nil?
crowbar["crowbar"]["public_name"] = value
@attributes_changed_for_event.add("public_name")
end
end

Expand Down Expand Up @@ -835,6 +841,20 @@ def _remove_elements_from_node(old, new, from_node)
@attrs_last_saved = @role.default_attributes.deep_dup

Rails.logger.debug("Done saving node: #{@node.name} - #{crowbar_revision}")

unless @attributes_changed_for_event.empty?
Rails.logger.debug("Notifying about saved node: #{@node.name}")

details = {
node: @node.name,
attributes: @attributes_changed_for_event
}
Crowbar::EventDispatcher.trigger_hooks(:node_changed, details)

@attributes_changed_for_event = Set.new

Rails.logger.debug("Done notifying about saved node: #{@node.name}")
end
end

def destroy
Expand Down
7 changes: 7 additions & 0 deletions crowbar_framework/app/models/service_object.rb
Expand Up @@ -1348,6 +1348,13 @@ def apply_role(role, inst, in_queue, bootstrap = false)
proposal.save unless roles_to_remove.empty?

update_proposal_status(inst, "success", "")

details = {
barclamp: @bc_name,
instance: inst
}
Crowbar::EventDispatcher.trigger_hooks(:proposal_applied, details)

[200, {}]
rescue StandardError => e
@logger.fatal("apply_role: Uncaught exception #{e.message} #{e.backtrace.join("\n")}")
Expand Down
48 changes: 48 additions & 0 deletions crowbar_framework/lib/crowbar/event_dispatcher.rb
@@ -0,0 +1,48 @@
#
# Copyright 2017, SUSE
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

module Crowbar
class EventDispatcher
class << self
def trigger_hooks(event, details)
BarclampCatalog.barclamps.keys.each do |barclamp|
begin
cls = ServiceObject.get_service(barclamp)
rescue NameError
# catalog may contain barclamps which don't have services
next
end

next unless cls.method_defined?(:event_hook)

service = cls.new(Rails.logger)

proposals = Proposal.where(barclamp: barclamp)
proposals.each do |proposal|
role = proposal.role
next if role.nil?
begin
service.event_hook(role, event, details)
rescue StandardError => e
raise "Error while executing event hook of #{barclamp} for #{event}: #{e.message}"
end
end
end
end
handle_asynchronously :trigger_hooks
end
end
end

0 comments on commit 83ad9a3

Please sign in to comment.