# Buggy code
def hostname():
    """
    Return fqdn, hostname, domainname
    """
    # This is going to need some work
    # Provides:
    #   fqdn
    #   host
    #   localhost
    #   domain
    global __FQDN__
    grains = {}

    if salt.utils.is_proxy():
        return grains

    grains["localhost"] = socket.gethostname()
    if __FQDN__ is None:
        __FQDN__ = salt.utils.network.get_fqhostname()
    grains["fqdn"] = __FQDN__
    (grains["host"], grains["domain"]) = grains["fqdn"].partition(".")[::2]
    return grains


# Traceback (AttributeError)
root@:/opt/salt/lib/python2.7/site-packages/salt/modules # /opt/salt/bin/salt-call --local test.ping
[ERROR   ] An un-handled exception was caught by salt's global exception handler:
AttributeError: 'NoneType' object has no attribute 'partition'
Traceback (most recent call last):
File "/opt/salt/bin/salt-call", line 11, in <module>
sys.exit(salt_call())
File "/opt/salt/lib/python2.7/site-packages/salt/scripts.py", line 361, in salt_call
client.run()
File "/opt/salt/lib/python2.7/site-packages/salt/cli/call.py", line 48, in run
caller = salt.cli.caller.Caller.factory(self.config)
File "/opt/salt/lib/python2.7/site-packages/salt/cli/caller.py", line 79, in factory
return ZeroMQCaller(opts, **kwargs)
File "/opt/salt/lib/python2.7/site-packages/salt/cli/caller.py", line 274, in __init__
super(ZeroMQCaller, self).__init__(opts)
File "/opt/salt/lib/python2.7/site-packages/salt/cli/caller.py", line 102, in __init__
self.minion = salt.minion.SMinion(opts)
File "/opt/salt/lib/python2.7/site-packages/salt/minion.py", line 627, in __init__
opts['grains'] = salt.loader.grains(opts)
File "/opt/salt/lib/python2.7/site-packages/salt/loader.py", line 703, in grains
ret = fun()
File "/opt/salt/lib/python2.7/site-packages/salt/grains/core.py", line 1616, in hostname
(grains['host'], grains['domain']) = grains['fqdn'].partition('.')[::2]
AttributeError: 'NoneType' object has no attribute 'partition'
Traceback (most recent call last):
File "/opt/salt/bin/salt-call", line 11, in <module>
sys.exit(salt_call())
File "/opt/salt/lib/python2.7/site-packages/salt/scripts.py", line 361, in salt_call
client.run()
File "/opt/salt/lib/python2.7/site-packages/salt/cli/call.py", line 48, in run
caller = salt.cli.caller.Caller.factory(self.config)
File "/opt/salt/lib/python2.7/site-packages/salt/cli/caller.py", line 79, in factory
return ZeroMQCaller(opts, **kwargs)
File "/opt/salt/lib/python2.7/site-packages/salt/cli/caller.py", line 274, in __init__
super(ZeroMQCaller, self).__init__(opts)
File "/opt/salt/lib/python2.7/site-packages/salt/cli/caller.py", line 102, in __init__
self.minion = salt.minion.SMinion(opts)
File "/opt/salt/lib/python2.7/site-packages/salt/minion.py", line 627, in __init__
opts['grains'] = salt.loader.grains(opts)
File "/opt/salt/lib/python2.7/site-packages/salt/loader.py", line 703, in grains
ret = fun()
File "/opt/salt/lib/python2.7/site-packages/salt/grains/core.py", line 1616, in hostname
(grains['host'], grains['domain']) = grains['fqdn'].partition('.')[::2]
AttributeError: 'NoneType' object has no attribute 'partition'

# Fixed code
def hostname():
    """
    Return fqdn, hostname, domainname
    """
    # This is going to need some work
    # Provides:
    #   fqdn
    #   host
    #   localhost
    #   domain
    global __FQDN__
    grains = {}

    if salt.utils.is_proxy():
        return grains

    grains["localhost"] = socket.gethostname()
    if __FQDN__ is None:
        __FQDN__ = salt.utils.network.get_fqhostname()

    # On some distros (notably FreeBSD) if there is no hostname set
    # salt.utils.network.get_fqhostname() will return None.
    # In this case we punt and log a message at error level, but force the
    # hostname and domain to be localhost.localdomain
    # Otherwise we would stacktrace below
    if __FQDN__ is None:  # still!
        log.error(
            "Having trouble getting a hostname.  Does this machine have its hostname and domain set properly?"
        )
        __FQDN__ = "localhost.localdomain"

    grains["fqdn"] = __FQDN__
    (grains["host"], grains["domain"]) = grains["fqdn"].partition(".")[::2]
    return grains