Repository: juju/python-libjuju

Issue:
Add hostname attribute to machine object
With https://github.com/juju/juju/pull/12754 being merged, and in 2.8.10, I'd like to access this new information via libjuju.  It would be great if we could have this available via machine.hostname, same as we can have machine.dns_name now.

Discussion:

Patch:
diff --git a/examples/machine_hostname.py b/examples/machine_hostname.py
new file mode 100644
index 0000000..787693f
--- /dev/null
+++ b/examples/machine_hostname.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3.5
+
+"""
+This example:
+
+1. Connects to the current model
+2. Creates a machine
+3. Waits for the machine agent to start and prints out the reported machine
+   hostname.
+
+NOTE: this example requires a 2.8.10+ controller.
+"""
+import logging
+
+from juju import loop
+from juju.model import Model
+
+MB = 1
+GB = 1024
+
+
+async def main():
+    model = Model()
+    await model.connect()
+
+    try:
+        # Add a machine and wait until the machine agents starts
+        machine1 = await model.add_machine()
+        await model.block_until(
+            lambda: machine1.agent_status == 'started')
+
+        # At this point we can access the reported hostname via the hostname
+        # property of the machine model.
+        print("machine1 hostname: {}".format(machine1.hostname))
+
+        await machine1.destroy(force=True)
+    finally:
+        await model.disconnect()
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.DEBUG)
+    ws_logger = logging.getLogger('websockets.protocol')
+    ws_logger.setLevel(logging.INFO)
+
+    loop.run(main())
diff --git a/juju/machine.py b/juju/machine.py
index eb5afa9..61dced9 100644
--- a/juju/machine.py
+++ b/juju/machine.py
@@ -322,6 +322,17 @@ class Machine(model.ModelEntity):
                 return addresses[0]['value']
         return None
 
+    @property
+    def hostname(self):
+        """Get the hostname for this machine as reported by the machine agent
+        running on it. This is only supported on 2.8.10+ controllers.
+
+        May return None if no hostname information is available.
+        """
+        if 'hostname' in self.safe_data and self.safe_data['hostname'] != '':
+            return self.safe_data['hostname']
+        return None
+
     @property
     def series(self):
         """Returns the series of the current machine


Test patch:
diff --git a/tests/unit/test_machine.py b/tests/unit/test_machine.py
new file mode 100644
index 0000000..74bdef4
--- /dev/null
+++ b/tests/unit/test_machine.py
@@ -0,0 +1,25 @@
+import asynctest
+import mock
+import pytest
+
+from juju.model import Model
+from juju.machine import Machine
+
+
+@asynctest.patch('juju.client.client.ClientFacade')
+@pytest.mark.asyncio
+async def test_hostname(mock_cf):
+    model = Model()
+    model._connector = mock.MagicMock()
+    model.state = mock.MagicMock()
+
+    # Calling hostname() when no information is available (e.g. targeting
+    # an older controller, agent not started yet etc.) should return None
+    model.state.entity_data = mock.MagicMock(return_value={})
+    mach = Machine('test', model)
+    assert mach.hostname is None
+
+    model.state.entity_data = mock.MagicMock(return_value={
+        'hostname': 'thundering-herds',
+    })
+    assert mach.hostname == 'thundering-herds'