freeipa/ipalib/plugins/automount.py

693 lines
22 KiB
Python

# Authors:
# Rob Crittenden <rcritten@redhat.com>
#
# Copyright (C) 2008 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
Frontend plugins for automount.
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
in autofs version 4.1.3-197. Any version prior to that is not expected
to work.
As an example, the following automount files:
auto.master:
/- auto.direct
/mnt auto.mnt
auto.mnt:
stuff -ro,soft,rsize=8192,wsize=8192 nfs.example.com:/vol/archive/stuff
are equivalent to the following LDAP entries:
# auto.master, automount, example.com
dn: automountmapname=auto.master,cn=automount,dc=example,dc=com
objectClass: automountMap
objectClass: top
automountMapName: auto.master
# auto.direct, automount, example.com
dn: automountmapname=auto.direct,cn=automount,dc=example,dc=com
objectClass: automountMap
objectClass: top
automountMapName: auto.direct
# /-, auto.master, automount, example.com
dn: automountkey=/-,automountmapname=auto.master,cn=automount,dc=example,dc=co
m
objectClass: automount
objectClass: top
automountKey: /-
automountInformation: auto.direct
# auto.mnt, automount, example.com
dn: automountmapname=auto.mnt,cn=automount,dc=example,dc=com
objectClass: automountMap
objectClass: top
automountMapName: auto.mnt
# /mnt, auto.master, automount, example.com
dn: automountkey=/mnt,automountmapname=auto.master,cn=automount,dc=example,dc=
com
objectClass: automount
objectClass: top
automountKey: /mnt
automountInformation: auto.mnt
# stuff, auto.mnt, automount, example.com
dn: automountkey=stuff,automountmapname=auto.mnt,cn=automount,dc=example,dc=com
objectClass: automount
objectClass: top
automountKey: stuff
automountInformation: -ro,soft,rsize=8192,wsize=8192 nfs.example.com:/vol/arch
ive/stuff
"""
from ldap import explode_dn
from ipalib import crud, errors
from ipalib import api, Str, Flag, Object, Command
map_attributes = ['automountMapName', 'description', ]
key_attributes = ['description', 'automountKey', 'automountInformation']
def display_entry(textui, entry):
# FIXME: for now delete dn here. In the future pass in the kw to
# output_for_cli()
attr = sorted(entry.keys())
for a in attr:
if a != 'dn':
textui.print_plain("%s: %s" % (a, entry[a]))
def make_automount_dn(mapname):
"""
Construct automount dn from map name.
"""
# FIXME, should this be in b_ldap?
# Experimenting to see what a plugin looks like for a 3rd party who can't
# modify the backend.
import ldap
return 'automountmapname=%s,%s,%s' % (
ldap.dn.escape_dn_chars(mapname),
api.env.container_automount,
api.env.basedn,
)
def make_ldap_map(ldapuri, mapname):
"""
Convert a map name into an LDAP name.
Note: This is unused currently. This would return map names as a
quasi ldap URL which will work with older autofs clients. We are
not currently supporting them.
"""
if not ldapuri:
return mapname
if mapname.find('ldap:') >= 0:
return mapname
return 'ldap:%s:%s' % (api.env.host, make_automount_dn(mapname))
class automount(Object):
"""
Automount object.
"""
takes_params = (
Str('automountmapname',
cli_name='mapname',
primary_key=True,
doc='A group of related automount objects',
),
)
api.register(automount)
class automount_addmap(crud.Add):
'Add a new automount map.'
takes_options = (
Str('description?',
doc='A description of the automount map'),
)
def execute(self, mapname, **kw):
"""
Execute the automount-addmap operation.
Returns the entry as it will be created in LDAP.
:param mapname: The map name being added.
:param kw: Keyword arguments for the other LDAP attributes.
"""
assert 'automountmapname' not in kw
assert 'dn' not in kw
ldap = self.api.Backend.ldap
kw['automountmapname'] = mapname
kw['dn'] = make_automount_dn(mapname)
kw['objectclass'] = ['automountMap']
return ldap.create(**kw)
def output_for_cli(self, textui, result, map, **options):
"""
Output result of this command to command line interface.
"""
textui.print_plain("Automount map %s added" % map)
api.register(automount_addmap)
class automount_addkey(crud.Add):
'Add a new automount key.'
takes_options = (
Str('automountkey',
cli_name='key',
doc='An entry in an automount map'),
Str('automountinformation',
cli_name='info',
doc='Mount information for this key'),
Str('description?',
doc='A description of the mount'),
)
def execute(self, mapname, **kw):
"""
Execute the automount-addkey operation.
Returns the entry as it will be created in LDAP.
:param mapname: The map name being added to.
:param kw: Keyword arguments for the other LDAP attributes.
"""
assert 'automountmapname' not in kw
assert 'dn' not in kw
ldap = self.api.Backend.ldap
config = ldap.get_ipa_config()
# use find_entry_dn instead of make_automap_dn so we can confirm that
# the map exists
map_dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
kw['dn'] = "automountkey=%s,%s" % (kw['automountkey'], map_dn)
kw['automountinformation'] = make_ldap_map(config.get('automountldapuri', False), kw['automountinformation'])
kw['objectclass'] = ['automount']
return ldap.create(**kw)
def output_for_cli(self, textui, result, *args, **options):
"""
Output result of this command to command line interface.
"""
textui.print_plain("Automount key added")
api.register(automount_addkey)
class automount_delmap(crud.Del):
'Delete an automount map.'
def execute(self, mapname, **kw):
"""Delete an automount map. This will also remove all of the keys
associated with this map.
mapname is the automount map to remove
:param mapname: The map to be removed
:param kw: Not used.
"""
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
# First remove all the keys for this map so we don't leave orphans
keys = api.Command['automount_getkeys'](mapname)
for k in keys:
ldap.delete(k.get('dn'))
# Now remove the parental connection
try:
infodn = ldap.find_entry_dn("automountinformation", mapname, "automount", api.env.container_automount)
ldap.delete(infodn)
except errors.NotFound:
# direct maps may not have this
pass
return ldap.delete(dn)
def output_for_cli(self, textui, result, *args, **options):
"""
Output result of this command to command line interface.
"""
print "Automount map and associated keys deleted"
api.register(automount_delmap)
class automount_delkey(crud.Del):
'Delete an automount key.'
takes_options = (
Str('automountkey',
cli_name='key',
doc='The automount key to remove'),
)
def execute(self, mapname, **kw):
"""Delete an automount key.
key is the automount key to remove
:param mapname: The automount map containing the key to be removed
:param kw: "key" the key to be removed
"""
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
keys = api.Command['automount_getkeys'](mapname)
keydn = None
keyname = kw.get('automountkey').lower()
if keys:
for k in keys:
if k.get('automountkey').lower() == keyname:
keydn = k.get('dn')
break
if not keydn:
raise errors.NotFound(reason='Removing keys failed. key %s not found' % keyname)
return ldap.delete(keydn)
def output_for_cli(self, textui, result, *args, **options):
"""
Output result of this command to command line interface.
"""
print "Automount key deleted"
api.register(automount_delkey)
class automount_modmap(crud.Mod):
'Edit an existing automount map.'
takes_options = (
Str('description?',
doc='A description of the automount map'),
)
def execute(self, mapname, **kw):
"""
Execute the automount-modmap operation.
The dn should not be passed as a keyword argument as it is constructed
by this method.
Returns the entry
:param mapname: The map name to update.
:param kw: Keyword arguments for the other LDAP attributes.
"""
assert 'automountmapname' not in kw
assert 'dn' not in kw
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
return ldap.update(dn, **kw)
def output_for_cli(self, textui, result, *args, **options):
"""
Output result of this command to command line interface.
"""
print "Automount map updated"
api.register(automount_modmap)
class automount_modkey(crud.Mod):
'Edit an existing automount key.'
takes_options = (
Str('automountkey',
cli_name='key',
doc='An entry in an automount map'),
Str('automountinformation?',
cli_name='info',
doc='Mount information for this key'),
Str('description?',
doc='A description of the automount map'),
)
def execute(self, mapname, **kw):
"""
Execute the automount-modkey operation.
Returns the entry
:param mapname: The map name to update.
:param kw: Keyword arguments for the other LDAP attributes.
"""
assert 'automountmapname' not in kw
assert 'dn' not in kw
keyname = kw.get('automountkey').lower()
del kw['automountkey']
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
keys = api.Command['automount_getkeys'](mapname)
keydn = None
if keys:
for k in keys:
if k.get('automountkey').lower() == keyname:
keydn = k.get('dn')
break
if not keydn:
raise errors.NotFound(reason='Update failed, unable to find key %s' % keyname)
return ldap.update(keydn, **kw)
def output_for_cli(self, textui, result, *args, **options):
"""
Output result of this command to command line interface.
"""
print "Automount key updated"
api.register(automount_modkey)
class automount_findmap(crud.Find):
'Search automount maps.'
takes_options = (
Flag('all', doc='Retrieve all attributes'),
)
def execute(self, term, **kw):
ldap = self.api.Backend.ldap
search_fields = map_attributes
for s in search_fields:
kw[s] = term
kw['objectclass'] = 'automountMap'
kw['base'] = api.env.container_automount
if kw.get('all', False):
kw['attributes'] = ['*']
else:
kw['attributes'] = map_attributes
return ldap.search(**kw)
def output_for_cli(self, textui, result, *args, **options):
counter = result[0]
entries = result[1:]
if counter == 0:
textui.print_plain("No entries found")
return
elif counter == -1:
textui.print_plain("These results are truncated.")
textui.print_plain("Please refine your search and try again.")
for e in entries:
display_entry(textui, e)
textui.print_plain("")
api.register(automount_findmap)
class automount_findkey(crud.Find):
'Search automount keys.'
takes_options = (
Flag('all?', doc='Retrieve all attributes'),
)
def get_args(self):
return (Str('automountkey',
cli_name='key',
doc='An entry in an automount map'),)
def execute(self, term, **kw):
ldap = self.api.Backend.ldap
search_fields = key_attributes
for s in search_fields:
kw[s] = term
kw['objectclass'] = 'automount'
kw['base'] = api.env.container_automount
if kw.get('all', False):
kw['attributes'] = ['*']
else:
kw['attributes'] = key_attributes
return ldap.search(**kw)
def output_for_cli(self, textui, result, *args, **options):
counter = result[0]
entries = result[1:]
if counter == 0:
textui.print_plain("No entries found")
return
elif counter == -1:
textui.print_plain("These results are truncated.")
textui.print_plain("Please refine your search and try again.")
for e in entries:
display_entry(textui, e)
textui.print_plain("")
api.register(automount_findkey)
class automount_showmap(crud.Get):
'Examine an existing automount map.'
takes_options = (
Flag('all?', doc='Retrieve all attributes'),
)
def execute(self, mapname, **kw):
"""
Execute the automount-showmap operation.
Returns the entry
:param mapname: The automount map to retrieve
:param kw: "all" set to True = return all attributes
"""
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
# FIXME: should kw contain the list of attributes to display?
if kw.get('all', False):
return ldap.retrieve(dn)
else:
return ldap.retrieve(dn, map_attributes)
def output_for_cli(self, textui, result, *args, **options):
if result:
display_entry(textui, result)
api.register(automount_showmap)
class automount_showkey(crud.Get):
'Examine an existing automount key.'
takes_options = (
Str('automountkey',
cli_name='key',
doc='The automount key to display'),
Flag('all?', doc='Retrieve all attributes'),
)
def execute(self, mapname, **kw):
"""
Execute the automount-showkey operation.
Returns the entry
:param mapname: The mapname to examine
:param kw: "automountkey" the key to retrieve
:param kw: "all" set to True = return all attributes
"""
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
keys = api.Command['automount_getkeys'](mapname)
keyname = kw.get('automountkey').lower()
keydn = None
if keys:
for k in keys:
if k.get('automountkey').lower() == keyname:
keydn = k.get('dn')
break
if not keydn:
raise errors.NotFound(reason='Unable to find key %s' % keyname)
# FIXME: should kw contain the list of attributes to display?
if kw.get('all', False):
return ldap.retrieve(keydn)
else:
return ldap.retrieve(keydn, key_attributes)
def output_for_cli(self, textui, result, *args, **options):
# The automount map name associated with this key is available only
# in the dn. Add it as an attribute to display instead.
if result and not result.get('automountmapname'):
elements = explode_dn(result.get('dn').lower())
for e in elements:
(attr, value) = e.split('=',1)
if attr == 'automountmapname':
result['automountmapname'] = value
display_entry(textui, result)
api.register(automount_showkey)
class automount_getkeys(Command):
'Retrieve all keys for an automount map.'
takes_args = (
Str('automountmapname',
cli_name='mapname',
primary_key=True,
doc='A group of related automount objects',
),
)
def execute(self, mapname, **kw):
"""
Execute the automount-getkeys operation.
Return a list of all automount keys for this mapname
:param mapname: Retrieve all keys for this mapname
"""
ldap = self.api.Backend.ldap
dn = ldap.find_entry_dn("automountmapname", mapname, "automountmap", api.env.container_automount)
try:
keys = ldap.get_one_entry(dn, 'objectclass=*', ['*'])
except errors.NotFound:
keys = []
return keys
def output_for_cli(self, textui, result, *args, **options):
for k in result:
textui.print_plain('%s' % k.get('automountkey'))
api.register(automount_getkeys)
class automount_getmaps(Command):
'Retrieve all automount maps'
takes_args = (
Str('automountmapname?',
cli_name='mapname',
primary_key=True,
doc='A group of related automount objects',
default=u'auto.master',
),
)
def execute(self, mapname, **kw):
"""
Execute the automount-getmaps operation.
Return a list of all automount maps.
"""
ldap = self.api.Backend.ldap
base = api.env.container_automount + "," + api.env.basedn
search_base = "automountmapname=%s,%s" % (mapname, base)
maps = ldap.get_one_entry(search_base, "objectClass=*", ["*"])
return maps
def output_for_cli(self, textui, result, *args, **options):
for k in result:
textui.print_plain('%s: %s' % (k.get('automountinformation'), k.get('automountkey')))
api.register(automount_getmaps)
class automount_addindirectmap(crud.Add):
"""
Add a new automap indirect mount point.
"""
takes_options = (
Str('parentmap?',
cli_name='parentmap',
default=u'auto.master',
autofill=True,
doc='The parent map to connect this to.',
),
Str('automountkey',
cli_name='key',
doc='An entry in an automount map',
),
Str('description?',
doc='A description of the automount map',
),
)
def execute(self, mapname, **kw):
"""
Execute the automount-addindirectmap operation.
Returns the key entry as it will be created in LDAP.
This function creates 2 LDAP entries. It creates an
automountmapname entry and an automountkey entry.
:param mapname: The map name being added.
:param kw['parentmap'] is the top-level map to add this to.
defaulting to auto.master
:param kw['automountkey'] is the mount point
:param kw['description'] is a textual description of this map
"""
mapkw = {}
if kw.get('description'):
mapkw['description'] = kw.get('description')
newmap = api.Command['automount_addmap'](mapname, **mapkw)
keykw = {'automountkey': kw['automountkey'], 'automountinformation': mapname}
if kw.get('description'):
keykw['description'] = kw.get('description')
newkey = api.Command['automount_addkey'](kw['parentmap'], **keykw)
return newkey
def output_for_cli(self, textui, result, map, **options):
"""
Output result of this command to command line interface.
"""
textui.print_plain("Indirect automount map %s added" % map)
api.register(automount_addindirectmap)
class automount_tofiles(Command):
'Generate the automount maps as they would be in the filesystem'
def execute(self, **kw):
"""
Execute the automount-getmaps operation.
Return a list of all automount maps.
"""
ldap = self.api.Backend.ldap
base = api.env.container_automount + "," + api.env.basedn
search_base = "automountmapname=auto.master,%s" % base
maps = ldap.get_one_entry(search_base, "objectClass=autoMount", ["*"])
mapkeys = {}
for m in maps:
keys = api.Command['automount_getkeys'](m.get('automountinformation').decode('UTF-8'))
mapkeys[m.get('automountinformation')] = keys
return maps, mapkeys
def output_for_cli(self, textui, result, **options):
maps = result[0]
keys = result[1]
textui.print_plain("/etc/auto.master:")
for m in maps:
textui.print_plain('%s\t/etc/%s' % (m.get('automountkey'), m.get('automountinformation')))
for m in maps:
textui.print_plain('---------------------------')
textui.print_plain('/etc/%s:' % m.get('automountinformation'))
mapkeys = keys.get(m.get('automountinformation'))
for k in mapkeys:
textui.print_plain('%s\t%s' % (k.get('automountkey'), k.get('automountinformation')))
api.register(automount_tofiles)