When the context property is given, use the NO_LOAD flag to stop Augeas loading
all files for all lenses.  Scan the /augeas/load//incl nodes to find those
relevant to the context given and then remove all others before loading.

This speeds up loading when the context is provided, as Augeas only reads and
parses relevant files, instead of every known file for each resource.

Signed-off-by: Dominic Cleal <[email protected]>
---
Local-branch: tickets/master/7285
 lib/puppet/provider/augeas/augeas.rb     |   55 +++++++++++++++++++++++++++++-
 spec/unit/provider/augeas/augeas_spec.rb |   49 ++++++++++++++++++++++++++
 2 files changed, 103 insertions(+), 1 deletions(-)

diff --git a/lib/puppet/provider/augeas/augeas.rb 
b/lib/puppet/provider/augeas/augeas.rb
index a16f54b..85dd6df 100644
--- a/lib/puppet/provider/augeas/augeas.rb
+++ b/lib/puppet/provider/augeas/augeas.rb
@@ -138,7 +138,14 @@ Puppet::Type.type(:augeas).provide(:augeas) do
     unless @aug
       flags = Augeas::NONE
       flags = Augeas::TYPE_CHECK if resource[:type_check] == :true
-      flags |= Augeas::NO_MODL_AUTOLOAD if resource[:incl]
+
+      if resource[:incl]
+        flags |= Augeas::NO_MODL_AUTOLOAD
+      elsif resource[:context] and resource[:context].match("^/files/.*")
+        # Optimise loading if the context is given
+        flags |= Augeas::NO_LOAD
+      end
+
       root = resource[:root]
       load_path = resource[:load_path]
       debug("Opening augeas with root #{root}, lens path #{load_path}, flags 
#{flags}")
@@ -150,6 +157,27 @@ Puppet::Type.type(:augeas).provide(:augeas) do
         aug.set("/augeas/load/Xfm/lens", resource[:lens])
         aug.set("/augeas/load/Xfm/incl", resource[:incl])
         aug.load
+      elsif resource[:context] and resource[:context].match("^/files/")
+        ctx_path = resource[:context].sub("/files", "")
+
+        # Use the context to identify all 'incl' nodes with globs that match
+        # the file we're editing.
+        incl_paths = {}
+        aug.match("/augeas/load//incl").each do |incl_augp|
+          incl_path = aug.get(incl_augp)
+          incl_paths[incl_augp] = incl_path if incl_path and 
glob_matches?(incl_path, ctx_path)
+        end
+
+        # Remove all incl nodes and add back the ones known to be appropriate
+        unless incl_paths.empty?
+          aug.rm("/augeas/load//incl")
+          incl_paths.each { |incl_augp,incl_path|
+            aug.set("%s[last()+1]" % incl_augp, incl_path)
+          }
+        else
+          debug("Augeas optimisation failed, unable to find #{ctx_path} in 
/augeas/load//incl")
+        end
+        aug.load
       end
     end
     @aug
@@ -163,6 +191,31 @@ Puppet::Type.type(:augeas).provide(:augeas) do
     end
   end
 
+  # Test if a glob from Augeas' incl nodes matches a given context path.
+  # Supporting full glob capabilities (a la POSIX, man 7 glob) is tricky and
+  # not used in Augeas yet.
+  def glob_matches?(glob, path)
+    glob = glob.gsub("*", ".*").gsub("?", ".")
+    rglob = Regexp.new(/^#{glob}$/)
+    cpath = path
+
+    # The context path may be more specific than the glob, so strip off the 
last
+    # path component until it matches
+    until cpath.empty?
+      return true if rglob.match(cpath)
+      cpath = cpath.sub(/\/[^\/]*$/, '')
+    end
+
+    # If the context path is less specific (e.g. /files/etc/sysconfig) then
+    # also match globs for files beneath this path
+    until glob.empty? or glob == "~"
+      return true if rglob.match(path)
+      glob = glob.sub(/\/[^\/]*$/, '')
+      rglob = Regexp.new(/^#{glob}$/)
+    end
+    false
+  end
+
   # Used by the need_to_run? method to process get filters. Returns
   # true if there is a match, false if otherwise
   # Assumes a syntax of get /files/path [COMPARATOR] value
diff --git a/spec/unit/provider/augeas/augeas_spec.rb 
b/spec/unit/provider/augeas/augeas_spec.rb
index 5bb98ea..a1928c9 100755
--- a/spec/unit/provider/augeas/augeas_spec.rb
+++ b/spec/unit/provider/augeas/augeas_spec.rb
@@ -487,4 +487,53 @@ describe provider_class do
       @provider.execute_changes.should == :executed
     end
   end
+
+  describe "incl glob matching" do
+    before do
+      @resource = stub("resource")
+      @provider = provider_class.new(@resource)
+    end
+
+    it "should match identical path" do
+      glob = "/etc/sysconfig/network"
+      path = "/etc/sysconfig/network"
+      @provider.glob_matches?(glob, path).should == true
+    end
+
+    it "should match path with trailing slash" do
+      glob = "/etc/sysconfig/network"
+      path = "/etc/sysconfig/network/"
+      @provider.glob_matches?(glob, path).should == true
+    end
+
+    it "should match simple * glob" do
+      glob = "/etc/sysconfig/*"
+      path = "/etc/sysconfig/network"
+      @provider.glob_matches?(glob, path).should == true
+    end
+
+    it "should match simple ? glob" do
+      glob = "/etc/sysconfi?/network"
+      path = "/etc/sysconfig/network"
+      @provider.glob_matches?(glob, path).should == true
+    end
+
+    it "should match over-specific path" do
+      glob = "/etc/sysconfig/network"
+      path = "/etc/sysconfig/network/HOSTNAME"
+      @provider.glob_matches?(glob, path).should == true
+    end
+
+    it "should not match different path" do
+      glob = "/etc/sysconfig/network"
+      path = "/etc/sysconfig/i18n"
+      @provider.glob_matches?(glob, path).should == false
+    end
+
+    it "should match unspecific path" do
+      glob = "/etc/sysconfig/network"
+      path = "/etc"
+      @provider.glob_matches?(glob, path).should == true
+    end
+  end
 end
-- 
1.7.4.4

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to