diff --git a/chef/cookbooks/barclamp/libraries/barclamp_library.rb b/chef/cookbooks/barclamp/libraries/barclamp_library.rb index 06927cee8d..d792ddcc7d 100644 --- a/chef/cookbooks/barclamp/libraries/barclamp_library.rb +++ b/chef/cookbooks/barclamp/libraries/barclamp_library.rb @@ -14,6 +14,7 @@ # require_relative "conduit_resolver.rb" +require "chef/role" module BarclampLibrary class Barclamp @@ -417,21 +418,96 @@ def self.size_to_bytes(s) end end + class DependsOn + class << self + def add(dependency) + @recipe_depedencies ||= Mash.new + @recipe_depedencies.merge!(dependency) + end + + def get(recipe) + @recipe_depedencies.fetch(recipe, []) + end + end + end + class Config class << self attr_accessor :node - def load(group, barclamp, instance = nil) - # If no instance is specified, see if this node uses an instance of - # this barclamp and use it + def last_two_configs(group, barclamp, instance) + prev_cfg = load(group, barclamp, instance) + instance = infer_instance(group, barclamp, instance) + cur_cfg = Chef::Role.load "#{barclamp}-config-#{instance}" + return prev_cfg, cur_cfg.default_attributes[barclamp] + end + + def loadattr(hash, attrlist) + i = 0 + return hash unless attrlist.length > 0 + while hash.respond_to?(:[]) + unless hash.has_key?(attrlist[i]) + Chef::Log.debug("[smart] hash is missing #{attrlist[i]}") + return nil + end + hash = hash[attrlist[i]] + i += 1 + if attrlist.length == i + # Chef::Log.debug("[smart] loadattr return #{hash}") + return hash + end + end + Chef::Log.debug("[smart] hash does not respond to []") + nil + end + + def guess_instance(barclamp, instance) if instance.nil? && @node[barclamp] && @node[barclamp][:config] instance = @node[barclamp][:config][:environment] end - # Accept environments passed as instances if instance =~ /^#{barclamp}-config-(.*)/ instance = $1 end + instance + end + + def infer_instance(group, barclamp, instance) + if instance.nil? + # try the "default" instance, and fallback on any existing instance + instance = "default" + unless @cache["groups"][group].fetch(instance, {}).key?(barclamp) + # sort to guarantee a consistent order + @cache["groups"][group].keys.sort.each do |key| + # ignore the id attribute from the data bag item, which is not + # an instance + next if key == "id" + if @cache["groups"][group][key].key?(barclamp) + instance = key + break + end + end + end + end + instance + end + + def changes_to_apply?(depedency, group = "openstack", instance = nil) + barclamp = depedency.shift + instance = guess_instance(barclamp, instance) + prev_cfg, curr_cfg = last_two_configs(group, barclamp, instance) + old = loadattr(prev_cfg, depedency) + new = loadattr(curr_cfg, depedency) + # Chef::Log.debug("[smart] loadattr prev #{depedency}, #{prev_cfg}") + # Chef::Log.debug("[smart] loadattr curr #{depedency}, #{curr_cfg.inspect}") + # Chef::Log.debug("[smart] comparision #{old}, #{new}") + old != new + end + + def load(group, barclamp, instance = nil) + # If no instance is specified, see if this node uses an instance of + # this barclamp and use it + instance = guess_instance(barclamp, instance) # Cache the config we load from data bag items. # This cache needs to be invalidated for each chef-client run from @@ -453,23 +529,7 @@ def load(group, barclamp, instance = nil) {} end - if instance.nil? - # try the "default" instance, and fallback on any existing instance - instance = "default" - unless @cache["groups"][group].fetch(instance, {}).key?(barclamp) - # sort to guarantee a consistent order - @cache["groups"][group].keys.sort.each do |key| - # ignore the id attribute from the data bag item, which is not - # an instance - next if key == "id" - if @cache["groups"][group][key].key?(barclamp) - instance = key - break - end - end - end - end - + instance = infer_instance(group, barclamp, instance) @cache["groups"][group].fetch(instance, {}).fetch(barclamp, {}) end end diff --git a/chef/cookbooks/barclamp/libraries/smartroles.rb b/chef/cookbooks/barclamp/libraries/smartroles.rb new file mode 100644 index 0000000000..d956af081c --- /dev/null +++ b/chef/cookbooks/barclamp/libraries/smartroles.rb @@ -0,0 +1,22 @@ +require "chef/mixin/language_include_recipe" + +class Chef + module Mixin + module LanguageIncludeRecipe + def include_recipe_smartly(*list_of_recipes) + list_of_recipes.each do |recipe| + included = false + BarclampLibrary::Barclamp::DependsOn.get(recipe).each do |dependency| + next unless BarclampLibrary::Barclamp::Config.changes_to_apply?(dependency) + Chef::Log.info("[smart] including recipe: #{recipe}") + Chef::Log.debug("[smart] due to change in: #{dependency}") + include_recipe recipe + included = true + break + end # each + Chef::Log.info("[smart] recipe excluded: #{recipe}") unless included + end # each + end # def include_recipe_smartly + end + end +end