Permalink
Browse files

Add mounttab provider for fstab

New provider for the mounttab type defined in puppetlabs-mount_providers.
It only supports the Fstab lens as used on Linux+BSD, not Vfstab as used on
Solaris.

This adds puppetlabs-mount_providers as a hard dependency.
  • Loading branch information...
1 parent 207ca75 commit 33dd3aeed498c03bcd864a7d2dd4ba9b3de15973 Dominic Cleal committed Aug 25, 2012
View
@@ -8,4 +8,4 @@ description 'This module provides alternative providers for core Puppet types us
project_page 'http://github.com/domcleal/augeasproviders'
## Add dependencies, if any:
-# dependency 'username/name', '>= 1.2.0'
+dependency 'puppetlabs/mount_providers'
View
@@ -1 +1,3 @@
forge "http://forge.puppetlabs.com"
+
+mod "puppetlabs/mount_providers"
View
@@ -1,2 +1,8 @@
+FORGE
+ remote: http://forge.puppetlabs.com
+ specs:
+ puppetlabs/mount_providers (0.0.1)
+
DEPENDENCIES
+ puppetlabs/mount_providers (>= 0)
View
@@ -49,6 +49,10 @@ The following builtin types have an Augeas-based provider implemented:
* `host`
* `mailalias`
+The following other types have a provider implemented:
+
+ * `mounttab` from [puppetlabs-mount_providers](http://forge.puppetlabs.com/puppetlabs/mount_providers)
+
The module adds the following new types:
* `sshd_config` for setting configuration entries in OpenSSH's `sshd_config`
@@ -65,7 +69,6 @@ See [Puppet/Augeas pre-requisites](http://projects.puppetlabs.com/projects/puppe
The following builtin types have Augeas-based providers planned:
* `ssh_authorized_key`
- * `mount` or `mounttab`, using puppetlabs-mount\_providers (see [#7188](http://projects.puppetlabs.com/issues/7188))
* `port`, once [#5660](http://projects.puppetlabs.com/issues/5660) is done
* `yumrepo`, once [#8758](http://projects.puppetlabs.com/issues/8758) is done
@@ -0,0 +1,310 @@
+# Alternative Augeas-based provider for mounttab, distributed in
+# puppetlabs-mount_providers.
+#
+# Copyright (c) 2012 Dominic Cleal
+# Licensed under the Apache License, Version 2.0
+
+require 'augeasproviders/provider'
+
+Puppet::Type.type(:mounttab).provide(:augeas) do
+ desc "Uses Augeas API to update the /etc/(v)fstab file"
+
+ def self.file(resource = nil)
+ file = "/etc/fstab"
+ file = resource[:target] if resource and resource[:target]
+ file.chomp("/")
+ end
+
+ confine :feature => :augeas
+ confine :exists => file
+
+ def self.augopen(resource = nil)
+ lens = case Facter.value(:operatingsystem)
+ when "Solaris"
+ fail("Solaris unsupported") # Vfstab.lns
+ else
+ "Fstab.lns"
+ end
+ AugeasProviders::Provider.augopen(lens, file(resource))
+ end
+
+ def self.instances
+ aug = nil
+ path = "/files#{file}"
+ begin
+ resources = []
+ aug = augopen
+ aug.match("#{path}/*").each do |mpath|
+ entry = {:ensure => :present}
+ entry[:name] = aug.get("#{mpath}/file")
+ next unless entry[:name]
+ entry[:device] = aug.get("#{mpath}/spec")
+ entry[:fstype] = aug.get("#{mpath}/vfstype")
+
+ options = []
+ aug.match("#{mpath}/opt").each do |apath|
+ options << aug.get(apath)
+ end
+ entry[:options] = options
+ entry[:pass] = aug.get("#{mpath}/passno") if aug.match("#{mpath}/passno")
+ entry[:dump] = aug.get("#{mpath}/dump") if aug.match("#{mpath}/dump")
+
+ resources << new(entry)
+ end
+ resources
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def exists?
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ not aug.match("#{path}/*[file = '#{resource[:name]}']").empty?
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def create
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.set("#{path}/01/spec", resource[:device])
+ aug.set("#{path}/01/file", resource[:name])
+ aug.set("#{path}/01/vfstype", resource[:fstype])
+
+ # Options are defined as a list property, so they get joined with commas.
+ # Since Augeas understands elements, access the original array or string.
+ opts = resource.original_parameters[:options]
+ if opts and not [opts].empty?
+ opts = [resource.original_parameters[:options]].flatten
+ opts.each do |opt|
+ optk, optv = opt.split("=", 2)
+ aug.set("#{path}/01/opt[last()+1]", optk)
+ aug.set("#{path}/01/opt[last()+1]/value", optv) if optv
+ end
+ else
+ # Strictly this is optional, but only Augeas > 0.10.0 has a lens that
+ # knows this is the case, so always fill it in.
+ aug.set("#{path}/01/opt", "defaults")
+ end
+
+ # FIXME: optional
+ aug.set("#{path}/01/dump", resource[:dump].to_s)
+ aug.set("#{path}/01/passno", resource[:pass].to_s)
+
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def destroy
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.rm("#{path}/*[file = '#{resource[:name]}']")
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def target
+ self.class.file(resource)
+ end
+
+ def device
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.get("#{path}/*[file = '#{resource[:name]}']/spec")
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def device=(value)
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.set("#{path}/*[file = '#{resource[:name]}']/spec", value)
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def fstype
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.get("#{path}/*[file = '#{resource[:name]}']/vfstype")
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def fstype=(value)
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.set("#{path}/*[file = '#{resource[:name]}']/vfstype", value)
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def options
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ opts = []
+ aug.match("#{path}/*[file = '#{resource[:name]}']/opt").each do |opath|
+ opt = aug.get(opath)
+ optv = aug.get("#{opath}/value")
+ opt = "#{opt}=#{optv}" if optv
+ opts << opt
+ end
+ opts.join(",")
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def options=(values)
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ entry = "#{path}/*[file = '#{resource[:name]}']"
+ begin
+ aug = self.class.augopen(resource)
+ aug.rm("#{entry}/opt")
+
+ insafter = "vfstype"
+ values = [resource.original_parameters[:options]].flatten
+
+ if values.empty?
+ # If options= is called, it will always be with a value (i.e. only when
+ # the user has specified the property), so always set defaults
+ aug.insert("#{entry}/#{insafter}", "opt", false)
+ aug.set("#{entry}/opt", "defaults")
+ else
+ values.each do |opt|
+ optk, optv = opt.split("=", 2)
+ aug.insert("#{entry}/#{insafter}", "opt", false)
+ aug.set("#{entry}/opt[last()]", optk)
+ aug.set("#{entry}/opt[last()]/value", optv) if optv
+ insafter = "opt[last()]"
+ end
+ end
+
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def dump
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.get("#{path}/*[file = '#{resource[:name]}']/dump")
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def dump=(value)
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+
+ # Ensure "defaults" option is always set if dump is being set, as the
+ # opts field is optional
+ if aug.match("#{path}/*[file = '#{resource[:name]}']/opt").empty?
+ aug.set("#{path}/*[file = '#{resource[:name]}']/opt", "defaults")
+ end
+
+ aug.set("#{path}/*[file = '#{resource[:name]}']/dump", value.to_s)
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def pass
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.get("#{path}/*[file = '#{resource[:name]}']/passno")
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def pass=(value)
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+
+ # Ensure "defaults" option is always set if passno is being set, as the
+ # opts field is optional
+ if aug.match("#{path}/*[file = '#{resource[:name]}']/opt").empty?
+ aug.set("#{path}/*[file = '#{resource[:name]}']/opt", "defaults")
+ end
+
+ # Ensure dump is always set too
+ if aug.match("#{path}/*[file = '#{resource[:name]}']/dump").empty?
+ aug.set("#{path}/*[file = '#{resource[:name]}']/dump", "0")
+ end
+
+ aug.set("#{path}/*[file = '#{resource[:name]}']/passno", value.to_s)
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def atboot
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ aug.get("#{path}/*[file = '#{resource[:name]}']/atboot")
+ ensure
+ aug.close if aug
+ end
+ end
+
+ def atboot=(value)
+ return # FIXME: not written
+ aug = nil
+ path = "/files#{self.class.file(resource)}"
+ begin
+ aug = self.class.augopen(resource)
+ # Ensure dump is always set if passno is being set
+ if aug.match("#{path}/*[file = '#{resource[:name]}']/dump").empty?
+ aug.set("#{path}/*[file = '#{resource[:name]}']/dump", "0")
+ end
+ aug.set("#{path}/*[file = '#{resource[:name]}']/passno", value.to_s)
+ aug.save!
+ ensure
+ aug.close if aug
+ end
+ end
+end
@@ -0,0 +1,17 @@
+brokenfile
+#
+# /etc/fstab
+# Created by anaconda on Mon Aug 16 11:08:41 2010
+#
+# Accessible filesystems, by reference, are maintained under '/dev/disk'
+# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
+#
+/dev/mapper/vgiridium-lvroot / ext4 noatime 1 1
+UUID=23b3b5f4-d5b3-4661-ad41-caa970f3ca59 /boot ext4 noatime 1 2
+/dev/mapper/luks-10f63ee4-8296-434e-8de1-cde932e8a2e1 /home ext4 noatime 1 2
+tmpfs /tmp tmpfs size=1024m 0 0
+UUID=7ce78ba2-1bba-49f4-9790-bfb6b2010bb1 swap swap defaults 0 0
+tmpfs /dev/shm tmpfs defaults 0 0
+devpts /dev/pts devpts gid=5,mode=620 0 0
+sysfs /sys sysfs defaults 0 0
+proc /proc proc defaults 0 0
No changes.
@@ -0,0 +1,17 @@
+
+#
+# /etc/fstab
+# Created by anaconda on Mon Aug 16 11:08:41 2010
+#
+# Accessible filesystems, by reference, are maintained under '/dev/disk'
+# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
+#
+/dev/mapper/vgiridium-lvroot / ext4 noatime
+UUID=23b3b5f4-d5b3-4661-ad41-caa970f3ca59 /boot ext4 noatime 1 2
+/dev/mapper/luks-10f63ee4-8296-434e-8de1-cde932e8a2e1 /home ext4 noatime 1 2
+tmpfs /tmp tmpfs size=1024m 0 0
+UUID=7ce78ba2-1bba-49f4-9790-bfb6b2010bb1 swap swap defaults 0 0
+tmpfs /dev/shm tmpfs defaults 0 0
+devpts /dev/pts devpts gid=5,mode=620 0 0
+sysfs /sys sysfs defaults 0 0
+proc /proc proc defaults 0 0
View
@@ -1,6 +1,6 @@
require 'pathname'
dir = Pathname.new(__FILE__).parent
-$LOAD_PATH.unshift(dir, dir + 'lib', dir + '../lib')
+$LOAD_PATH.unshift(dir, File.join(dir, 'lib'), File.join(dir, '..', 'lib'))
require 'rubygems'
@@ -13,3 +13,5 @@
RSpec.configure do |config|
config.mock_with :mocha
end
+
+Puppet[:modulepath] = File.join(dir, '..', 'modules')
Oops, something went wrong.

0 comments on commit 33dd3ae

Please sign in to comment.