Ejemplo n.º 1
0
  def testUpdateNoMaster(self):
    """An update skips updating the master map, and approprate sub maps."""
    source_entry1 = automount.AutomountMapEntry()
    source_entry2 = automount.AutomountMapEntry()
    source_entry1.key = '/home'
    source_entry2.key = '/auto'
    source_entry1.location = 'ou=auto.home,ou=automounts'
    source_entry2.location = 'ou=auto.auto,ou=automounts'
    source_master = automount.AutomountMap([source_entry1, source_entry2])

    local_entry1 = automount.AutomountMapEntry()
    local_entry2 = automount.AutomountMapEntry()
    local_entry1.key = '/home'
    local_entry2.key = '/auto'
    local_entry1.location = '/etc/auto.home'
    local_entry2.location = '/etc/auto.null'
    local_master = automount.AutomountMap([local_entry1, local_entry2])

    source_mock = self.mox.CreateMock(source.Source)
    # return the source master map
    source_mock.GetAutomountMasterMap().AndReturn(source_master)

    # the auto.home cache
    cache_home = self.mox.CreateMock(caches.Cache)
    # GetMapLocation() is called, and set to the master map map_entry
    cache_home.GetMapLocation().AndReturn('/etc/auto.home')

    # the auto.auto cache
    cache_auto = self.mox.CreateMock(caches.Cache)
    # GetMapLocation() is called, and set to the master map map_entry
    cache_auto.GetMapLocation().AndReturn('/etc/auto.auto')

    # the auto.master cache, which should not be written to
    cache_master = self.mox.CreateMock(caches.Cache)
    cache_master.GetMap().AndReturn(local_master)

    self.mox.StubOutWithMock(cache_factory, 'Create')
    cache_factory.Create(mox.IgnoreArg(), mox.IgnoreArg(), automount_mountpoint=None).AndReturn(cache_master)
    cache_factory.Create(mox.IgnoreArg(), mox.IgnoreArg(), automount_mountpoint='/home').AndReturn(cache_home)
    cache_factory.Create(mox.IgnoreArg(), mox.IgnoreArg(), automount_mountpoint='/auto').AndReturn(cache_auto)

    skip = map_updater.AutomountUpdater.OPT_LOCAL_MASTER
    updater = map_updater.AutomountUpdater(
        config.MAP_AUTOMOUNT, self.workdir, {skip: 'yes'})

    self.mox.StubOutClassWithMocks(map_updater, 'MapUpdater')
    updater_home = map_updater.MapUpdater(config.MAP_AUTOMOUNT, self.workdir, {'local_automount_master': 'yes'}, automount_mountpoint='/home')
    updater_home.UpdateCacheFromSource(cache_home, source_mock, True, False, 'ou=auto.home,ou=automounts').AndReturn(0)

    self.mox.ReplayAll()

    updater.UpdateFromSource(source_mock)
Ejemplo n.º 2
0
  def testUpdate(self):
    """An update gets a master map and updates each entry."""
    map_entry1 = automount.AutomountMapEntry()
    map_entry2 = automount.AutomountMapEntry()
    map_entry1.key = '/home'
    map_entry2.key = '/auto'
    map_entry1.location = 'ou=auto.home,ou=automounts'
    map_entry2.location = 'ou=auto.auto,ou=automounts'
    master_map = automount.AutomountMap([map_entry1, map_entry2])

    source_mock = self.mox.CreateMock(source.Source)
    # return the master map
    source_mock.GetAutomountMasterMap().AndReturn(master_map)

    # the auto.home cache
    cache_home = self.mox.CreateMock(caches.Cache)
    # GetMapLocation() is called, and set to the master map map_entry
    cache_home.GetMapLocation().AndReturn('/etc/auto.home')

    # the auto.auto cache
    cache_auto = self.mox.CreateMock(caches.Cache)
    # GetMapLocation() is called, and set to the master map map_entry
    cache_auto.GetMapLocation().AndReturn('/etc/auto.auto')

    # the auto.master cache
    cache_master = self.mox.CreateMock(caches.Cache)

    self.mox.StubOutWithMock(cache_factory, 'Create')
    cache_factory.Create(mox.IgnoreArg(), 'automount', automount_mountpoint='/home').AndReturn(cache_home)
    cache_factory.Create(mox.IgnoreArg(), 'automount', automount_mountpoint='/auto').AndReturn(cache_auto)
    cache_factory.Create(mox.IgnoreArg(), 'automount', automount_mountpoint=None).AndReturn(cache_master)

    updater = map_updater.AutomountUpdater(
        config.MAP_AUTOMOUNT, self.workdir, {})

    self.mox.StubOutClassWithMocks(map_updater, 'MapUpdater')
    updater_home = map_updater.MapUpdater(config.MAP_AUTOMOUNT, self.workdir, {}, automount_mountpoint='/home')
    updater_home.UpdateCacheFromSource(cache_home, source_mock, True, False, 'ou=auto.home,ou=automounts').AndReturn(0)
    updater_auto = map_updater.MapUpdater(config.MAP_AUTOMOUNT, self.workdir, {}, automount_mountpoint='/auto')
    updater_auto.UpdateCacheFromSource(cache_auto, source_mock, True, False, 'ou=auto.auto,ou=automounts').AndReturn(0)
    updater_master = map_updater.MapUpdater(config.MAP_AUTOMOUNT, self.workdir, {})
    updater_master.FullUpdateFromMap(cache_master, master_map).AndReturn(0)

    self.mox.ReplayAll()

    updater.UpdateFromSource(source_mock)

    self.assertEqual(map_entry1.location, '/etc/auto.home')
    self.assertEqual(map_entry2.location, '/etc/auto.auto')
Ejemplo n.º 3
0
    def UpdateFromSource(self, source, incremental=True, force_write=False):
        """Update this map's cache from the source provided.

    The FileMapUpdater expects to fetch as single map from the source
    and write/merge it to disk.  We create a cache to write to, and then call
    UpdateCacheFromSource() with that cache.

    Note that AutomountUpdater also calls UpdateCacheFromSource() for each
    cache it is writing, hence the distinct seperation.

    Args:
      source: A nss_cache.sources.Source object.
      incremental: A boolean flag indicating that an incremental update should
        be performed, defaults to True.
      force_write: A boolean flag forcing empty map updates, defaults to False.

    Returns:
      An int indicating success of update (0 == good, fail otherwise).
    """
        # Create the single cache we write to
        cache = cache_factory.Create(self.cache_options, self.map_name)

        return self.UpdateCacheFromSource(cache,
                                          source,
                                          incremental,
                                          force_write,
                                          location=None)
Ejemplo n.º 4
0
    def testGetSingleMapMetadata(self):
        # test both automount and non-automount maps.

        # cache mock is returned by FakeCreate() for automount maps
        cache_mock = self.mox.CreateMock(caches.Cache)
        cache_mock.GetMapLocation().AndReturn('/etc/auto.master')

        self.mox.StubOutWithMock(cache_factory, 'Create')
        cache_factory.Create(
            self.conf.options[config.MAP_AUTOMOUNT].cache,
            config.MAP_AUTOMOUNT,
            automount_mountpoint='automount_mountpoint').AndReturn(cache_mock)

        self.mox.ReplayAll()

        c = command.Status()

        values = c.GetSingleMapMetadata(config.MAP_PASSWORD, self.conf)
        self.failUnless('map' in values[0])
        self.failUnless('key' in values[0])
        self.failUnless('value' in values[0])

        values = c.GetSingleMapMetadata(
            config.MAP_AUTOMOUNT,
            self.conf,
            automount_mountpoint='automount_mountpoint')

        self.failUnless('map' in values[0])
        self.failUnless('key' in values[0])
        self.failUnless('value' in values[0])
        self.failUnless('automount' in values[0])
Ejemplo n.º 5
0
  def testUpdateCatchesMissingMaster(self):
    """Gracefully handle a missing local master maps."""
    # use an empty master map from the source, to avoid mocking out already
    # tested code
    master_map = automount.AutomountMap()

    source_mock = self.mox.CreateMockAnything()
    source_mock.GetAutomountMasterMap().AndReturn(master_map)

    cache_mock = self.mox.CreateMock(caches.Cache)
    # raise error on GetMap()
    cache_mock.GetMap().AndRaise(error.CacheNotFound)

    skip = map_updater.AutomountUpdater.OPT_LOCAL_MASTER
    cache_options = {skip: 'yes'}

    self.mox.StubOutWithMock(cache_factory, 'Create')
    cache_factory.Create(
        cache_options, 'automount',
        automount_mountpoint=None).AndReturn(cache_mock)

    self.mox.ReplayAll()

    updater = map_updater.AutomountUpdater(
        config.MAP_AUTOMOUNT, self.workdir, cache_options)

    return_value = updater.UpdateFromSource(source_mock)

    self.assertEqual(return_value, 1)
Ejemplo n.º 6
0
    def testUpdateSingleMaps(self):
        self.mox.StubOutClassWithMocks(lock, 'PidFile')
        lock_mock = lock.PidFile(filename=None)
        lock_mock.Lock(force=False).AndReturn(True)
        lock_mock.Locked().AndReturn(True)
        lock_mock.Unlock()

        self.conf.maps = [config.MAP_PASSWORD]
        self.conf.cache = 'dummy'

        modify_stamp = 1
        map_entry = passwd.PasswdMapEntry({'name': 'foo', 'uid': 10, 'gid': 10})
        passwd_map = passwd.PasswdMap([map_entry])
        passwd_map.SetModifyTimestamp(modify_stamp)

        source_mock = self.mox.CreateMock(source.Source)
        source_mock.GetMap(config.MAP_PASSWORD,
                           location=None).AndReturn(passwd_map)

        self.mox.StubOutWithMock(source_factory, 'Create')
        source_factory.Create(self.conf.options[
            config.MAP_PASSWORD].source).AndReturn(source_mock)

        cache_mock = self.mox.CreateMock(caches.Cache)
        cache_mock.WriteMap(map_data=passwd_map).AndReturn(0)

        self.mox.StubOutWithMock(cache_factory, 'Create')
        cache_factory.Create(self.conf.options[config.MAP_PASSWORD].cache,
                             config.MAP_PASSWORD).AndReturn(cache_mock)

        self.mox.ReplayAll()
        c = command.Update()
        self.assertEqual(
            0, c.UpdateMaps(self.conf, incremental=True, force_write=False))
Ejemplo n.º 7
0
    def UpdateCacheFromSource(self,
                              cache,
                              source,
                              incremental=False,
                              force_write=False,
                              location=None):
        """Update a single cache file, from a given source.

        Args:
          cache: A nss_cache.caches.Cache object.
          source: A nss_cache.sources.Source object.
          incremental: We ignore this.
          force_write: A boolean flag forcing empty map updates when False,
            defaults to False.
          location: The optional location in the source of this map used by
            automount to specify which automount map to get, defaults to None.

        Returns:
          An int indicating the success of an update (0 == good, fail otherwise).
        """
        return_val = 0

        cache_filename = cache.GetCacheFilename()
        if cache_filename is not None:
            new_file_fd, new_file = tempfile.mkstemp(
                dir=os.path.dirname(cache_filename),
                prefix=os.path.basename(cache_filename),
                suffix='.nsscache.tmp')
        else:
            raise error.CacheInvalid('Cache has no filename.')

        self.log.debug('temp source filename: %s', new_file)
        try:
            # Writes the source to new_file.
            # Current file is passed in to allow the source to do partial diffs.
            # TODO(jaq): refactor this to pass in the whole cache, so that the source
            # can decide how to reduce downloads, c.f. last-modify-timestamp for ldap.
            source.GetFile(self.map_name,
                           new_file,
                           current_file=cache.GetCacheFilename(),
                           location=location)
            os.lseek(new_file_fd, 0, os.SEEK_SET)
            # TODO(jaq): this sucks.
            source_cache = cache_factory.Create(self.cache_options,
                                                self.map_name)
            source_map = source_cache.GetMap(new_file)

            # Update the cache from the new file.
            return_val += self._FullUpdateFromFile(cache, source_map,
                                                   force_write)
        finally:
            try:
                os.unlink(new_file)
            except OSError as e:
                # If we're using zsync source, it already renames the file for us.
                if e.errno != errno.ENOENT:
                    raise

        return return_val
Ejemplo n.º 8
0
    def testUpdateAutomounts(self):
        self.mox.StubOutClassWithMocks(lock, 'PidFile')
        lock_mock = lock.PidFile(filename=None)
        lock_mock.Lock(force=False).AndReturn(True)
        lock_mock.Locked().AndReturn(True)
        lock_mock.Unlock()

        self.conf.maps = [config.MAP_AUTOMOUNT]
        self.conf.cache = 'dummy'

        modify_stamp = 1
        map_entry = automount.AutomountMapEntry()
        map_entry.key = '/home'
        map_entry.location = 'foo'
        automount_map = automount.AutomountMap([map_entry])
        automount_map.SetModifyTimestamp(modify_stamp)

        source_mock = self.mox.CreateMock(source.Source)
        source_mock.GetAutomountMasterMap().AndReturn(automount_map)
        source_mock.GetMap(config.MAP_AUTOMOUNT,
                           location='foo').AndReturn(automount_map)

        self.mox.StubOutWithMock(source_factory, 'Create')
        source_factory.Create(self.conf.options[
            config.MAP_PASSWORD].source).AndReturn(source_mock)

        cache_mock = self.mox.CreateMock(caches.Cache)
        cache_mock.GetMapLocation().AndReturn('home')
        cache_mock.WriteMap(map_data=automount_map).AndReturn(0)
        cache_mock.WriteMap(map_data=automount_map).AndReturn(0)

        self.mox.StubOutWithMock(cache_factory, 'Create')
        cache_factory.Create(
            self.conf.options[config.MAP_AUTOMOUNT].cache,
            config.MAP_AUTOMOUNT,
            automount_mountpoint='/home').AndReturn(cache_mock)
        cache_factory.Create(self.conf.options[config.MAP_AUTOMOUNT].cache,
                             config.MAP_AUTOMOUNT,
                             automount_mountpoint=None).AndReturn(cache_mock)

        self.mox.ReplayAll()

        c = command.Update()
        self.assertEquals(
            0, c.UpdateMaps(self.conf, incremental=True, force_write=False))
Ejemplo n.º 9
0
    def GetSingleMapMetadata(self,
                             map_name,
                             conf,
                             automount_mountpoint=None,
                             epoch=False):
        """Return metadata from map specified.

        Args:
          map_name: name of map to extract data from
          conf: a config.Config object
          automount_mountpoint: information necessary for automount maps
          epoch: return times as an integer epoch (time_t) instead of a
            human readable name

        Returns:
          a list of dicts of metadata key/value pairs
        """
        cache_options = conf.options[map_name].cache

        updater = map_updater.MapUpdater(map_name, conf.timestamp_dir,
                                         cache_options, automount_mountpoint)

        modify_dict = {'key': 'last-modify-timestamp', 'map': map_name}
        update_dict = {'key': 'last-update-timestamp', 'map': map_name}
        if map_name == config.MAP_AUTOMOUNT:
            # have to find out *which* automount map from a cache object!
            cache = cache_factory.Create(
                cache_options,
                config.MAP_AUTOMOUNT,
                automount_mountpoint=automount_mountpoint)
            automount = cache.GetMapLocation()
            modify_dict['automount'] = automount
            update_dict['automount'] = automount

        last_modify_timestamp = updater.GetModifyTimestamp() or 0
        last_update_timestamp = updater.GetUpdateTimestamp() or 0

        if not epoch:
            # If we are displaying the time as a string, do so in localtime.  This is
            # the only place such a conversion is appropriate.
            if last_modify_timestamp:
                last_modify_timestamp = time.asctime(
                    time.localtime(last_modify_timestamp))
            else:
                last_modify_timestamp = 'Unknown'
            if last_update_timestamp:
                last_update_timestamp = time.asctime(
                    time.localtime(last_update_timestamp))
            else:
                last_update_timestamp = 'Unknown'

        modify_dict['value'] = last_modify_timestamp
        update_dict['value'] = last_update_timestamp

        return [modify_dict, update_dict]
Ejemplo n.º 10
0
    def testGetAutomountMapMetadata(self):
        # need to stub out GetSingleMapMetadata (tested above) and then
        # stub out cache_factory.Create to return a cache mock that spits
        # out an iterable map for the function to use.

        # stub out GetSingleMapMetadata
        class DummyStatus(command.Status):

            def GetSingleMapMetadata(self,
                                     unused_map_name,
                                     unused_conf,
                                     automount_mountpoint=None,
                                     epoch=False):
                return {
                    'map': 'map_name',
                    'last-modify-timestamp': 'foo',
                    'last-update-timestamp': 'bar'
                }

        # the master map to loop over
        master_map = automount.AutomountMap()
        master_map.Add(
            automount.AutomountMapEntry({
                'key': '/home',
                'location': '/etc/auto.home'
            }))
        master_map.Add(
            automount.AutomountMapEntry({
                'key': '/auto',
                'location': '/etc/auto.auto'
            }))

        # mock out a cache to return the master map
        cache_mock = self.mox.CreateMock(caches.Cache)
        cache_mock.GetMap().AndReturn(master_map)

        self.mox.StubOutWithMock(cache_factory, 'Create')
        cache_factory.Create(self.conf.options[config.MAP_AUTOMOUNT].cache,
                             config.MAP_AUTOMOUNT,
                             automount_mountpoint=None).AndReturn(cache_mock)

        self.mox.ReplayAll()

        c = DummyStatus()
        value_list = c.GetAutomountMapMetadata(self.conf)

        self.assertEqual(9, len(value_list))
Ejemplo n.º 11
0
    def testVerifyMapsException(self):
        cache_mock = self.mox.CreateMock(caches.Cache)
        cache_mock.GetMap().AndRaise(error.CacheNotFound)

        self.mox.StubOutWithMock(cache_factory, 'Create')
        cache_factory.Create(self.conf.options[config.MAP_PASSWORD].cache,
                             config.MAP_PASSWORD).AndReturn(cache_mock)

        self.conf.maps = [config.MAP_PASSWORD]

        self.mox.StubOutWithMock(nss, 'GetMap')
        nss.GetMap(config.MAP_PASSWORD).AndReturn(self.small_map)

        self.mox.ReplayAll()

        c = command.Verify()

        self.assertEquals(1, c.VerifyMaps(self.conf))
Ejemplo n.º 12
0
    def testVerifyMapsSucceedsOnGoodMaps(self):
        cache_mock = self.mox.CreateMock(caches.Cache)
        cache_mock.GetMap().AndReturn(self.small_map)

        self.mox.StubOutWithMock(cache_factory, 'Create')
        cache_factory.Create(self.conf.options[config.MAP_PASSWORD].cache,
                             config.MAP_PASSWORD).AndReturn(cache_mock)

        self.conf.maps = [config.MAP_PASSWORD]

        self.mox.StubOutWithMock(nss, 'GetMap')
        nss.GetMap(config.MAP_PASSWORD).AndReturn(self.big_map)

        self.mox.ReplayAll()

        c = command.Verify()

        self.assertEqual(0, c.VerifyMaps(self.conf))
Ejemplo n.º 13
0
    def GetAutomountMapMetadata(self, conf, epoch=False):
        """Return status of automount master map and all listed automount maps.

        We retrieve the automount master map, and build a list of dicts which
        are used by the caller to print the status output.

        Args:
          conf: a config.Config object
          epoch: return times as an integer epoch (time_t) instead of a
            human readable name

        Returns:
          a list of dicts of metadata key/value pairs
        """
        map_name = config.MAP_AUTOMOUNT
        cache_options = conf.options[map_name].cache
        value_list = []

        # get the value_dict for the master map, note that automount_mountpoint=None
        # defaults to the master map!
        values = self.GetSingleMapMetadata(map_name,
                                           conf,
                                           automount_mountpoint=None,
                                           epoch=epoch)
        value_list.extend(values)

        # now get the contents of the master map, and get the status for each map
        # we find
        cache = cache_factory.Create(cache_options,
                                     config.MAP_AUTOMOUNT,
                                     automount_mountpoint=None)
        master_map = cache.GetMap()

        for map_entry in master_map:
            values = self.GetSingleMapMetadata(
                map_name,
                conf,
                automount_mountpoint=map_entry.key,
                epoch=epoch)
            value_list.extend(values)
        return value_list
Ejemplo n.º 14
0
  def VerifyMaps(self, conf):
    """Compare each configured map against data retrieved from NSS.

    For each configured map, build a Map object from NSS and compare
    it against a Map object retrieved directly from the cache.  We
    expect the cache Map to be a subset of the nss Map due to possible
    inclusion of other NSS map types (e.g. files, nis, ldap, etc).

    This could be done via series of get*nam calls, however at this
    time it appears to be more efficient to grab them in bulk and use
    the Map.__contains__() membership test.

    Args:
      conf: nss_cache.config.Config object

    Returns:
      count of failures when verifying
    """
    retval = 0

    for map_name in conf.maps:
      self.log.info('Verifying map: %s.', map_name)

      # The netgroup map does not have an enumerator,
      # to test this we'd have to loop over the loaded cache map
      # and verify each entry is retrievable via getent directly.
      # TODO(blaed): apply fix from comment to allow for netgroup checking
      if map_name == config.MAP_NETGROUP:
        self.log.info(('The netgroup map does not support enumeration, '
                       'skipping.'))
        continue

      # Automount maps do not support getent, we'll have to come up with
      # a good way to verify these.
      if map_name == config.MAP_AUTOMOUNT:
        self.log.info(('The automount map does not support enumeration, '
                       'skipping.'))
        continue

      try:
        nss_map = nss.GetMap(map_name)
      except error.UnsupportedMap:
        self.log.warning('Verification of %s map is unsupported!', map_name)
        continue

      self.log.debug('built NSS map of %d entries', len(nss_map))

      cache_options = conf.options[map_name].cache
      cache = cache_factory.Create(cache_options, map_name)

      try:
        cache_map = cache.GetMap()
      except error.CacheNotFound:
        self.log.error('Cache missing!')
        retval +=1
        continue

      self.log.debug('built cache map of %d entries', len(cache_map))

      # cache_map is a subset of nss_map due to possible other maps,
      # e.g. files, nis, ldap, etc.
      missing_entries = 0
      for map_entry in cache_map:
        if map_entry not in nss_map:
          self.log.info('The following entry is present in the cache '
                        'but not availible via NSS! %s', map_entry.name)
          self.log.debug('missing entry data: %s', map_entry)
          missing_entries += 1

      if missing_entries > 0:
        self.log.warning('Missing %d entries in %s map',
                         missing_entries, map_name)
        retval +=1

    return retval
Ejemplo n.º 15
0
  def UpdateFromSource(self, source, incremental=False, force_write=False):
    """Update the automount master map, and every map it points to.

    We fetch a full copy of the master map everytime, and then use the
    FileMapUpdater to write each map the master map points to, as well
    as the master map itself.

    During this process, the master map will be modified.  It starts
    out pointing to other maps in the source, but when written it needs
    to point to other maps in the cache instead.  For example, using ldap we
    store this data in ldap:

    map_entry.key = /auto
    map_entry.location = ou=auto.auto,ou=automounts,dc=example,dc=com

    We need to go back to ldap get the map in ou=auto.auto, but when it comes
    time to write the master map to (for example) a file, we need to write
    out the /etc/auto.master file with:

    map_entry.key = /auto
    map_entry.location = /etc/auto.auto

    This is annoying :)  Since the keys are fixed, namely /auto is a mountpoint
    that isn't going to change format, we expect each Cache implementation that
    supports automount maps to support a GetMapLocation() method which returns
    the correct cache location from the key.

    Args:
      source: An nss_cache.sources.Source object.
      incremental: Not used by this class
      force_write: A boolean flag forcing empty map updates when False,
        defaults to False.

    Returns:
      An int indicating success of update (0 == good, fail otherwise).
    """
    return_val = 0

    try:
      if not self.local_master:
        self.log.info('Retrieving automount master map.')
        master_file = source.GetAutomountMasterFile(
            os.path.join(self.cache_options['dir'], 'auto.master'))
      master_cache = cache_factory.Create(self.cache_options, self.map_name,
                                          None)
      master_map = master_cache.GetMap()
    except error.CacheNotFound:
      return 1

    if self.local_master:
      self.log.info('Using local master map to determine maps to update.')
      # we need the local map to determine which of the other maps to update
      cache = cache_factory.Create(self.cache_options, self.map_name,
                                   automount_mountpoint=None)
      try:
        local_master = cache.GetMap()
      except error.CacheNotFound:
        self.log.warning('Local master map specified but no map found! '
                         'No maps will update.')
        return return_val + 1

    # update specific maps, e.g. auto.home and auto.auto
    for map_entry in master_map:
      source_location = os.path.basename(map_entry.location)
      mountpoint = map_entry.key        # e.g. /auto mountpoint
      self.log.debug('Looking at mountpoint %s', mountpoint)

      # create the cache to update
      cache = cache_factory.Create(self.cache_options,
                                   self.map_name,
                                   automount_mountpoint=mountpoint)

      # update the master map with the location of the map in the cache
      # e.g. /etc/auto.auto replaces ou=auto.auto
      map_entry.location = cache.GetMapLocation()
      self.log.debug('Map location: %s', map_entry.location)

      # if configured to use the local master map, skip any not defined there
      if self.local_master:
        if map_entry not in local_master:
          self.log.info('Skipping entry %s, not in map %s',
                        map_entry, local_master)
          continue
      self.log.info('Updating mountpoint %s', map_entry.key)
      # update this map (e.g. /etc/auto.auto)
      update_obj = FileMapUpdater(self.map_name,
                                  self.timestamp_dir,
                                  self.cache_options,
                                  automount_mountpoint=mountpoint)
      return_val += update_obj.UpdateCacheFromSource(cache, source, False,
                                                     force_write, source_location)
    # with sub-maps updated, write modified master map to disk if configured to
    if not self.local_master:
      # automount_mountpoint=None defaults to master
      cache = cache_factory.Create(self.cache_options,
                                   self.map_name,
                                   automount_mountpoint=None)
      update_obj = FileMapUpdater(self.map_name,
                                  self.timestamp_dir,
                                  self.cache_options)
      return_val += update_obj.FullUpdateFromMap(cache, master_file)

    return return_val