Call tearDown() on all instantiated plugs.

    Note that initialize_plugs must have been called before calling
    this method, and initialize_plugs must be called again after calling
    this method if you want to access the plugs attribute again.

    Any exceptions in tearDown() methods are logged, but do not get raised
    by this method.

def tear_down_plugs(self):
    """Call tearDown() on all instantiated plugs.

    Note that initialize_plugs must have been called before calling
    this method, and initialize_plugs must be called again after calling
    this method if you want to access the plugs attribute again.

    Any exceptions in tearDown() methods are logged, but do not get raised
    by this method.
    """
    _LOG.debug('Tearing down all plugs.')
    for plug_type, plug_instance in six.iteritems(self._plugs_by_type):
      if plug_instance.uses_base_tear_down():
        name = '<PlugTearDownThread: BasePlug No-Op for %s>' % plug_type
      else:
        name = '<PlugTearDownThread: %s>' % plug_type
      thread = _PlugTearDownThread(plug_instance, name=name)
      thread.start()
      timeout_s = (conf.plug_teardown_timeout_s
                   if conf.plug_teardown_timeout_s
                   else None)
      thread.join(timeout_s)
      if thread.is_alive():
        thread.kill()
        _LOG.warning('Killed tearDown for plug %s after timeout.',
                     plug_instance)
    self._plugs_by_type.clear()
    self._plugs_by_name.clear()