diff --git a/README.md b/README.md index 866707b..2d727c1 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,8 @@ An example using almost every possible parameter: # list of installed drivers. enabled => true, # Enabled by default shared => false, # Disabled by default - options => { media => 'A4' }, # Hash of options ( name => value ), highly depends on the printer. + options => { media => 'A4' }, # Hash of options ( name => value ), these are non vendor specific options. + ppd_options => { 'HPOption_Duplexer' => 'False' }, # Hash of vendor PPD options } - The easiest way to find out a list of valid options for any single printer is to install that printer locally, and diff --git a/lib/puppet/provider/printer/cups.rb b/lib/puppet/provider/printer/cups.rb index 0fb43aa..fa8bf0c 100644 --- a/lib/puppet/provider/printer/cups.rb +++ b/lib/puppet/provider/printer/cups.rb @@ -76,6 +76,7 @@ def self.prefetch(resources) printer[:ensure] = :present printer[:options] = self.printer_options(name, resource) + printer[:ppd_options] = self.ppd_options(name, resource) printer[:provider] = :cups printer[:uri] = prefetched_uris[printer[:name]] if prefetched_uris.key?(printer[:name]) @@ -132,7 +133,8 @@ def self.printers_long end - # Retrieve options explicitly specified by the type definition. + # I only retrieve options that are specified in the type definition, which avoids resetting all options at once. + # queue options are space separated, and come in pairs separated by equals(=) def self.printer_options(destination, resource) options = {} @@ -147,6 +149,27 @@ def self.printer_options(destination, resource) options end + # vendor PPD options are formatted differently: + # ShortName/Long Name: *Selected NotSelectedValue + def self.ppd_options(destination, resource) + ppdopts = {} + + return ppdopts unless resource[:ppd_options].kind_of? Hash + + lpoptions('-d', destination, '-l').each_line do |line| + keyvalues = line.split(':') + key = /^([^\/]*)/.match(keyvalues[0]).captures[0] + + next unless resource[:ppd_options].key? key + + selected_value = /\s\*([^\s]*)\s/.match(keyvalues[1]).captures[0] + + ppdopts[key] = selected_value + end + + ppdopts + end + def self.printer_uris begin uris = {} @@ -186,20 +209,21 @@ def flush lpadmin "-x", name when :present # Regardless of whether the printer is being added or modified, the `lpadmin -p` command is used. + # Sometimes, in the case of `-E` or `-o` parameters, lpadmin seems to do nothing under some circumstances. + # For this reason, I'm running cupsenable/reject and lpoptions to ensure those values match what we expect. # BUG: flush should never be called if only the model or PPD parameters differ, because lpstat can't tell # what the actual value is. params = Array.new # lpadmin parameters options = Array.new # lpoptions parameters + vendor_options = Array.new # ppd options # Handle most parameters via string substitution Cups_Options.keys.each do |k| params.unshift Cups_Options[k] % @resource[k] if @resource[k] end - - options.push '-o printer-is-shared=true' if @property_hash[:shared] === :true if @property_hash[:options].is_a? Hash @@ -214,6 +238,12 @@ def flush end end + if @property_hash[:ppd_options].is_a? Hash + @property_hash[:ppd_options].each_pair do |k, v| + vendor_options.push "-o %s=%s" % [k, v] + end + end + begin # -E means different things when it comes before or after -p, see man page for explanation. if @property_hash[:enabled] === :true and @property_hash[:accept] === :true @@ -226,20 +256,23 @@ def flush lpoptions "-p", name, options end - # -E option covers enable & accept both true, and allows us to skip the other utilities. - #unless @property_hash[:enabled] === :true and @property_hash[:accept] === :true - if @property_hash[:enabled] === :true - cupsenable name - else - cupsdisable name - end + unless vendor_options.empty? + lpoptions "-p", name, vendor_options + end - if @property_hash[:accept] === :true - cupsaccept name - else - cupsreject name - end - #end + # Normally, the -E option would let us skip cupsenable/accept. + # But the behaviour seems unpredictable when the queue already exists. + if @property_hash[:enabled] === :true + cupsenable name + else + cupsdisable name + end + + if @property_hash[:accept] === :true + cupsaccept name + else + cupsreject name + end rescue Exception => e # If an option turns out to be invalid, CUPS will normally add the printer anyway. # Normally, the printer should not even be created, so we delete it again to make things consistent. diff --git a/lib/puppet/type/printer.rb b/lib/puppet/type/printer.rb index be02255..db61b7f 100644 --- a/lib/puppet/type/printer.rb +++ b/lib/puppet/type/printer.rb @@ -59,13 +59,23 @@ end newproperty(:options) do - desc "Sets a list of PPD options for the printer" + desc "Sets a list of options for the printer" validate do |value| raise ArgumentError, "invalid value supplied for printer options" unless value.is_a? Hash end end + newproperty(:ppd_options) do + desc "Sets a list of PPD (vendor specific) options for the printer. + + Use lpoptions -p destination -l to get a list of valid vendor PPD options for that queue." + + validate do |value| + raise ArgumentError, "invalid value supplied for printer PPD options" unless value.is_a? Hash + end + end + # Allow a printer resource without explicitly specifying a file resource for the PPD. autorequire(:file) do self[:ppd] if self[:ppd]