Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
diff --git a/lib/puppet/provider/package/yum.rb b/lib/puppet/provider/package/yum.rb
index 581a446..d78a705 100755
--- a/lib/puppet/provider/package/yum.rb
+++ b/lib/puppet/provider/package/yum.rb
@@ -104,5 +104,14 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
     def purge
         yum "-y", :erase, @resource[:name]
     end
+
+    # Static function that enable multiple package to be install at the same time
+    def self.combine(resource_names)
+
+        # TODO Add lots of test and error handling
+
+        output = yum "-d", "0", "-e", "0", "-y", :install, resource_names
+
+    end
  end

diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index 37f51b2..4ba0ecc 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -219,7 +219,11 @@ class Transaction

             # Perform the actual changes
             seconds = thinmark do
-                events += apply(resource)
+                if combineable?(resource)
+                    events += combine(resource)
+                else
+                    events += apply(resource)
+                end
             end

             if children and ! resource.depthfirst?
@@ -473,7 +477,101 @@ class Transaction
             end
         end
     end
+
+    # Test if a specific resource can be combined, only package resources can be combined so far
+    # 
+    # Retrun true if it can be combined false otherwise
+    # * its provider should support combine
+    # * it should not be skiped
+    # * the combine attribute should be set to true
+    # * puppet is not running on noop mode
+    # * the packet is ensure (present, latest or installed), version is not supported for multiple install yet.
+    def combineable?(resource)
+        if  resource.provider.class.respond_to?(:combine) and 
+            ! resource.noop and
+            ! skip?(resource) and
+            resource[:combine] == :true and
+            [:present,:latest,:installed].include?(resource[:ensure]) and
+            changes = resource.evaluate and
+            allow_processing?(resource,changes) and
+            ! changes.empty?
+                 puts resource.to_yaml
+                 return true
+        else
+            return false
+        end
+    end
+       
+    # Function that install all the package available of the same class in a single package manager call if it is possible to combine them.
+    def combine(resource)
+
+        provider_class = resource.provider.class
+  
+        @multiple_install_events ||= {}
+        @multiple_install_events[provider_class] ||= {}
+  
+        if events = @multiple_install_events[provider_class][resource.name]
+            return events
+        end
+   
+        # Find all resources matching the provider class that set combining
+        resources = catalog.vertices.reject { |r|
+
+            !r.provider.class.equal?(provider_class) or !combineable?(r)
+        
+        # All the resource that will be installed
+        }.collect { |r|
+
+            changes = r.evaluate
+            changes = [changes] unless changes.is_a?(Array)
+
+            if changes.length > 0
+                @resourcemetrics[:out_of_sync] += 1
+            end
+
+            changes.each { |c|
+                @changes << c
+                @count += 1
+            }
+
+            resourceevents = [Puppet::Transaction::Event.new(r.name, r)]
+  
+            # Record when we last synced
+            r.cache(:synced, Time.now)

+            # Flush, if appropriate
+            if r.respond_to?(:flush)
+                r.flush
+            end
+            
+            # And set a trigger for refreshing this resource if it's a
+            # self-refresher
+            if r.self_refresh? and ! r.deleting?
+                # Create an edge with this resource as both the source and
+                # target.  The triggering method treats these specially for
+                # logging.
+                events = resourceevents.collect { |e| e.name }
+                set_trigger(Puppet::Relationship.new(r,r, :callback => :refresh, :event => events))
+            end
+  
+            @multiple_install_events[provider_class][r.name] = resourceevents
+  
+            r
+        }
+
+        # collecting their names in an array
+        resource_names = resources.collect { |r|
+            r[:name]
+        }
+  
+        # Add error handling ...
+        if resource_names.length > 0
+            output = provider_class.combine(resource_names)
+        end
+  
+        @multiple_install_events[provider_class][resource.name]
+    end    
+ 
     # Prepare to evaluate the resources in a transaction.
     def prepare
         # Now add any dynamically generated resources
diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb
index 655f9e0..2c507ed 100644
--- a/lib/puppet/type/package.rb
+++ b/lib/puppet/type/package.rb
@@ -277,6 +277,14 @@ module Puppet
             newvalues(:true, :false)
         end

+        newparam(:combine) do
+            desc "Tells package provider if the packet installation have to be
+                perform in a single call by the packet manager or if it has to
+                be executed alone."
+            defaultto :false
+            newvalues(:true, :false)
+        end
+
         autorequire(:file) do
             autos = []
             [:responsefile, :adminfile].each { |param|