summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins/automount.py
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2010-09-24 15:17:14 -0400
committerAdam Young <ayoung@redhat.com>2010-10-12 18:07:26 -0400
commit0197ebbb7b20252eaa08d644c9cb5fcc7c715022 (patch)
tree1c2a3cb45690aaf240f18c1a032d0e0411e29f86 /ipalib/plugins/automount.py
parentcf21396345c2b0c858693133aac80c639350e728 (diff)
downloadfreeipa-0197ebbb7b20252eaa08d644c9cb5fcc7c715022.tar.gz
freeipa-0197ebbb7b20252eaa08d644c9cb5fcc7c715022.tar.xz
freeipa-0197ebbb7b20252eaa08d644c9cb5fcc7c715022.zip
Add ability to import automount files from the command-line.
Support is fairly basic right now and will only work on the CLI. All the work is done on the client side. To continue past errors use the --continue option. Fixed a bug where direct mounts weren't always added properly. Added real user documentation to the plugin. ticket 78
Diffstat (limited to 'ipalib/plugins/automount.py')
-rw-r--r--ipalib/plugins/automount.py277
1 files changed, 271 insertions, 6 deletions
diff --git a/ipalib/plugins/automount.py b/ipalib/plugins/automount.py
index d9436a2bc..df9b34111 100644
--- a/ipalib/plugins/automount.py
+++ b/ipalib/plugins/automount.py
@@ -20,10 +20,92 @@
"""
Automount
+Stores automount(8) configuration for autofs(8) in IPA.
+
+The base of an automount configuration is the configuration file auto.master.
+This is also the base location in IPA. Multiple auto.master configurations
+can be stored in separate locations. A location is implementation-specific
+with the default being a location named 'default'. For example, you can have
+locations by geographic region, by floor, by type, etc.
+
+Automount has three basic object types: locations, maps and keys.
+
+A location defines a set of maps anchored in auto.master. This allows you
+to store multiple automount configurations. A location in itself isn't
+very interesting, it is just a point to start a new automount map.
+
+A map is roughly equivalent to discrete automount files. It is storage
+location for keys.
+
+A key is a mount point associated to a map.
+
+When a new location is created two maps are automatically created for
+it: auto.master and auto.direct. auto.master is the root map for all
+automount maps for the location. auto.direct is the default map for
+direct mounts and is mounted on /-.
+
+EXAMPLES:
+
+Locations:
+
+ Create a named location, "Baltimore":
+ ipa automountlocation-add baltimore
+
+ Display the new locations:
+ ipa automountlocation-show baltimore
+
+ Find available locations:
+ ipa automountlocation-find
+
+ Remove a named automount location:
+ ipa automountlocation-del baltimore
+
+ Show what the automount maps would look like if they were in the filesystem:
+ ipa automountlocation-tofiles baltimore
+
+ Import an existing configuration into a location:
+ ipa automountlocation-import baltimore /etc/auto.master
+
+ The import will fail if any duplicate entries are found. For
+ continous operation where errors are ignored use the --continue
+ option.
+
+Maps:
+
+ Create a new map, "auto.share":
+ ipa automountmap-add baltimore auto.share
+
+ Display the new map:
+ ipa automountmap-show baltimore auto.share
+
+ Find maps in the location baltimore:
+ ipa automountmap-find baltimore
+
+ Remove the auto.share map:
+ ipa automountmap-del baltimore auto.share
+
+Keys:
+
+ Create a new key for the auto.share map in location baltimore. This ties
+ the map we previously created to auto.master:
+ ipa automountkey-add baltimore auto.master /share --info=auto.share
+
+ Create a new key for our auto.share map, an NFS mount for man pages:
+ ipa automountkey-add baltimore auto.share man --info="-ro,soft,rsize=8192,wsize=8192 ipa.example.com:/shared/man"
+
+ Find all keys for the auto.share map:
+ ipa automountkey-find baltimore auto.share
+
+ Remove the man key from the auto.share map:
+ ipa automountkey-del baltimore auto.share man
+"""
+
+"""
+Developer notes:
+
RFC 2707bis http://www.padl.com/~lukeh/rfc2307bis.txt
A few notes on automount:
-- It was a design decision to not support different maps by location
- The default parent when adding an indirect map is auto.master
- This uses the short format for automount maps instead of the
URL format. Support for ldap as a map source in nsswitch.conf was added
@@ -89,6 +171,7 @@ from ipalib import Object, Command
from ipalib import Flag, Str
from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
+import os
class automountlocation(LDAPObject):
@@ -161,6 +244,8 @@ class automountlocation_tofiles(LDAPQuery):
def execute(self, *args, **options):
ldap = self.obj.backend
+ location = self.api.Command['automountlocation_show'](args[0])
+
maps = []
result = self.api.Command['automountkey_find'](
cn=args[0], automountmapname=u'auto.master'
@@ -175,8 +260,9 @@ class automountlocation_tofiles(LDAPQuery):
keys = {}
for m in maps:
info = m['automountinformation'][0]
+ key = info.split(None)
result = self.api.Command['automountkey_find'](
- cn=args[0], automountmapname=info
+ cn=args[0], automountmapname=key[0]
)
truncated = result['truncated']
keys[info] = result['result']
@@ -190,12 +276,21 @@ class automountlocation_tofiles(LDAPQuery):
textui.print_plain('/etc/auto.master:')
for m in maps:
- textui.print_plain(
- '%s\t/etc/%s' % (
- m['automountkey'][0], m['automountinformation'][0]
+ if m['automountinformation'][0].startswith('-'):
+ textui.print_plain(
+ '%s\t%s' % (
+ m['automountkey'][0], m['automountinformation'][0]
+ )
+ )
+ else:
+ textui.print_plain(
+ '%s\t/etc/%s' % (
+ m['automountkey'][0], m['automountinformation'][0]
+ )
)
- )
for m in maps:
+ if m['automountinformation'][0].startswith('-'):
+ continue
info = m['automountinformation'][0]
textui.print_plain('---------------------------')
textui.print_plain('/etc/%s:' % info)
@@ -209,6 +304,176 @@ class automountlocation_tofiles(LDAPQuery):
api.register(automountlocation_tofiles)
+class automountlocation_import(LDAPQuery):
+ """
+ Import automount files for a specific location.
+ """
+
+ takes_args = (
+ Str('masterfile',
+ label=_('Master file'),
+ doc=_('Automount master file'),
+ ),
+ )
+
+ takes_options = (
+ Flag('continue?',
+ cli_name='continue',
+ doc=_('Continous operation mode. Errors are reported but the process continues'),
+ ),
+ )
+
+ def __read_mapfile(self, filename):
+ try:
+ fp = open(filename, 'r')
+ map = fp.readlines()
+ fp.close()
+ except IOError, e:
+ if e.errno == 2:
+ raise errors.NotFound(reason=_('File %(file)s not found' % {'file':filename}))
+ else:
+ raise e
+ return map
+
+ def forward(self, *args, **options):
+ """
+ The basic idea is to read the master file and create all the maps
+ we need, then read each map file and add all the keys for the map.
+ """
+ location = self.api.Command['automountlocation_show'](args[0])
+
+ result = {'maps':[], 'keys':[], 'skipped':[], 'duplicatekeys':[], 'duplicatemaps':[]}
+ maps = {}
+ master = self.__read_mapfile(args[1])
+ for m in master:
+ if m.startswith('#'):
+ continue
+ m = m.rstrip()
+ if m.startswith('+'):
+ result['skipped'].append([m,args[1]])
+ continue
+ if len(m) == 0:
+ continue
+ am = m.split(None)
+ if am[1].startswith('/'):
+ mapfile = am[1].replace('"','')
+ am[1] = os.path.basename(am[1])
+ maps[am[1]] = mapfile
+ info = ' '.join(am[1:])
+
+ # Add a new key to the auto.master map for the new map file
+ try:
+ api.Command['automountkey_add'](cn=args[0], automountmapname=u'auto.master', automountkey=unicode(am[0]), automountinformation=unicode(' '.join(am[1:])))
+ result['keys'].append([am[0], u'auto.master'])
+ except errors.DuplicateEntry, e:
+ if options.get('continue', False):
+ result['duplicatekeys'].append(am[0])
+ pass
+ else:
+ raise errors.DuplicateEntry(message=unicode('key %(key)s already exists' % {'key':am[0]}))
+ # Add the new map
+ if not am[1].startswith('-'):
+ try:
+ api.Command['automountmap_add'](cn=args[0], automountmapname=unicode(am[1]))
+ result['maps'].append(am[1])
+ except errors.DuplicateEntry, e:
+ if options.get('continue', False):
+ result['duplicatemaps'].append(am[0])
+ pass
+ else:
+ raise errors.DuplicateEntry(message=unicode('map %(map)s already exists' % {'map':am[1]}))
+ except errors.DuplicateEntry:
+ # This means the same map is used on several mount points.
+ pass
+
+ # Now iterate over the map files and add the keys. To handle
+ # continuation lines I'll make a pass through it to skip comments
+ # etc and also to combine lines.
+ for m in maps:
+ map = self.__read_mapfile(maps[m])
+ lines = []
+ cont = ''
+ for x in map:
+ if x.startswith('#'):
+ continue
+ x = x.rstrip()
+ if x.startswith('+'):
+ result['skipped'].append([m, maps[m]])
+ continue
+ if len(x) == 0:
+ continue
+ if x.endswith("\\"):
+ cont = cont + x[:-1] + ' '
+ else:
+ lines.append(cont + x)
+ cont=''
+ for x in lines:
+ am = x.split(None)
+ key = unicode(am[0].replace('"',''))
+ try:
+ api.Command['automountkey_add'](cn=args[0], automountmapname=unicode(m), automountkey=key, automountinformation=unicode(' '.join(am[1:])))
+ result['keys'].append([key,m])
+ except errors.DuplicateEntry, e:
+ if options.get('continue', False):
+ result['duplicatekeys'].append(am[0])
+ pass
+ else:
+ raise e
+
+ return dict(result=result)
+
+ def output_for_cli(self, textui, result, *keys, **options):
+ maps = result['result']['maps']
+ keys = result['result']['keys']
+ duplicatemaps = result['result']['duplicatemaps']
+ duplicatekeys = result['result']['duplicatekeys']
+ skipped = result['result']['skipped']
+
+ textui.print_plain('Imported maps:')
+ for m in maps:
+ textui.print_plain(
+ 'Added %s' % m
+ )
+ textui.print_plain('')
+
+ textui.print_plain('Imported keys:')
+ for k in keys:
+ textui.print_plain(
+ 'Added %s to %s' % (
+ k[0], k[1]
+ )
+ )
+ textui.print_plain('')
+
+ textui.print_plain('Ignored keys:')
+ for k in keys:
+ textui.print_plain(
+ 'Ignored %s to %s' % (
+ k[0], k[1]
+ )
+ )
+
+
+ if options.get('continue', False) and len(duplicatemaps) > 0:
+ textui.print_plain('')
+ textui.print_plain('Duplicate maps skipped:')
+ for m in duplicatemaps:
+ textui.print_plain(
+ 'Skipped %s' % m
+ )
+
+
+ if options.get('continue', False) and len(duplicatekeys) > 0:
+ textui.print_plain('')
+ textui.print_plain('Duplicate keys skipped:')
+ for k in duplicatekeys:
+ textui.print_plain(
+ 'Skipped %s' % k
+ )
+
+
+api.register(automountlocation_import)
+
class automountmap(LDAPObject):
"""
Automount map object.