diff --git a/api-ref/source/hosts.inc b/api-ref/source/hosts.inc
new file mode 100644
index 000000000..c4ecafa99
--- /dev/null
+++ b/api-ref/source/hosts.inc
@@ -0,0 +1,45 @@
+.. -*- rst -*-
+
+==================
+Manage zun host
+==================
+
+List all compute hosts
+========================================
+
+.. rest_method::  GET /v1/hosts
+
+Enables administrative users to list all Zun container hosts.
+
+Response Codes
+--------------
+
+.. rest_status_code:: success status.yaml
+
+   - 200
+
+.. rest_status_code:: error status.yaml
+
+   - 400
+   - 401
+   - 403
+
+Response Parameters
+-------------------
+
+.. rest_parameters:: parameters.yaml
+
+   - X-Openstack-Request-Id: request_id
+   - hosts: host_list
+   - uuid: uuid
+   - hostname: hostname
+   - mem_total: mem_total
+   - cpus: cpus
+   - os: os
+   - labels: labels
+
+Response Example
+----------------
+
+.. literalinclude:: samples/host-get-all-resp.json
+   :language: javascript
diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst
index ee5e6e2cd..42af96d65 100644
--- a/api-ref/source/index.rst
+++ b/api-ref/source/index.rst
@@ -10,3 +10,4 @@
 .. include:: containers.inc
 .. include:: images.inc
 .. include:: services.inc
+.. include:: hosts.inc
diff --git a/api-ref/source/samples/host-get-all-resp.json b/api-ref/source/samples/host-get-all-resp.json
new file mode 100644
index 000000000..3a589183e
--- /dev/null
+++ b/api-ref/source/samples/host-get-all-resp.json
@@ -0,0 +1,23 @@
+{
+   "hosts": [
+       {
+           "hostname": "testhost",
+           "uuid": "d0405f06-101e-4340-8735-d1bf9fa8b8ad",
+           "links": [{
+               "href": "http://192.168.2.200:9517/v1/hosts/d0405f06-101e-4340-8735-d1bf9fa8b8ad",
+               "rel": "self"
+               },
+                {"href": "http://192.168.2.200:9517/hosts/d0405f06-101e-4340-8735-d1bf9fa8b8ad",
+                 "rel": "bookmark"
+                }
+           ],
+           "labels": {
+               "type": "test"
+           },
+           "cpus": 48,
+           "mem_total": 128446,
+           "os": "CentOS Linux 7 (Core)"
+       }
+   ],
+   "next": null
+}
diff --git a/etc/zun/policy.json b/etc/zun/policy.json
index 9e708378a..c24e1d842 100644
--- a/etc/zun/policy.json
+++ b/etc/zun/policy.json
@@ -38,5 +38,7 @@
     "zun-service:disable": "rule:admin_api",
     "zun-service:enable": "rule:admin_api",
     "zun-service:force_down": "rule:admin_api",
-    "zun-service:get_all": "rule:admin_api"
+    "zun-service:get_all": "rule:admin_api",
+
+    "host:get_all": "rule:admin_api"
 }
diff --git a/zun/api/controllers/v1/__init__.py b/zun/api/controllers/v1/__init__.py
index 246ef4550..2509d959f 100644
--- a/zun/api/controllers/v1/__init__.py
+++ b/zun/api/controllers/v1/__init__.py
@@ -24,6 +24,7 @@ import pecan
 from zun.api.controllers import base as controllers_base
 from zun.api.controllers import link
 from zun.api.controllers.v1 import containers as container_controller
+from zun.api.controllers.v1 import hosts as host_controller
 from zun.api.controllers.v1 import images as image_controller
 from zun.api.controllers.v1 import zun_services
 from zun.api.controllers import versions as ver
@@ -63,7 +64,8 @@ class V1(controllers_base.APIBase):
         'links',
         'services',
         'containers',
-        'images'
+        'images',
+        'hosts'
     )
 
     @staticmethod
@@ -97,6 +99,12 @@ class V1(controllers_base.APIBase):
                                     pecan.request.host_url,
                                     'images', '',
                                     bookmark=True)]
+        v1.hosts = [link.make_link('self', pecan.request.host_url,
+                                   'hosts', ''),
+                    link.make_link('bookmark',
+                                   pecan.request.host_url,
+                                   'hosts', '',
+                                   bookmark=True)]
         return v1
 
 
@@ -106,6 +114,7 @@ class Controller(controllers_base.Controller):
     services = zun_services.ZunServiceController()
     containers = container_controller.ContainersController()
     images = image_controller.ImagesController()
+    hosts = host_controller.HostController()
 
     @pecan.expose('json')
     def get(self):
diff --git a/zun/api/controllers/v1/hosts.py b/zun/api/controllers/v1/hosts.py
new file mode 100644
index 000000000..6551d5200
--- /dev/null
+++ b/zun/api/controllers/v1/hosts.py
@@ -0,0 +1,85 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_log import log as logging
+import pecan
+
+from zun.api.controllers import base
+from zun.api.controllers.v1 import collection
+from zun.api.controllers.v1.views import hosts_view as view
+from zun.api import utils as api_utils
+from zun.common import exception
+from zun.common import policy
+from zun import objects
+
+LOG = logging.getLogger(__name__)
+
+
+class HostCollection(collection.Collection):
+    """API representation of a collection of hosts."""
+
+    fields = {
+        'hosts',
+        'next'
+    }
+
+    """A list containing compute node objects"""
+
+    def __init__(self, **kwargs):
+        super(HostCollection, self).__init__(**kwargs)
+        self._type = 'hosts'
+
+    @staticmethod
+    def convert_with_links(nodes, limit, url=None,
+                           expand=False, **kwargs):
+        collection = HostCollection()
+        collection.hosts = [view.format_host(url, p) for p in nodes]
+        collection.next = collection.get_next(limit, url=url, **kwargs)
+        return collection
+
+
+class HostController(base.Controller):
+    """Host info controller"""
+
+    @pecan.expose('json')
+    @base.Controller.api_version("1.3")
+    @exception.wrap_pecan_controller_exception
+    def get_all(self, **kwargs):
+        """Retrieve a list of hosts"""
+        context = pecan.request.context
+        policy.enforce(context, "host:get_all",
+                       action="host:get_all")
+        return self._get_host_collection(**kwargs)
+
+    def _get_host_collection(self, **kwargs):
+        context = pecan.request.context
+        limit = api_utils.validate_limit(kwargs.get('limit'))
+        sort_dir = api_utils.validate_sort_dir(kwargs.get('sort_dir', 'asc'))
+        sort_key = kwargs.get('sort_key', 'hostname')
+        expand = kwargs.get('expand')
+        filters = None
+        marker_obj = None
+        resource_url = kwargs.get('resource_url')
+        marker = kwargs.get('marker')
+        if marker:
+            marker_obj = objects.ComputeNode.get_by_uuid(context, marker)
+        nodes = objects.ComputeNode.list(context,
+                                         limit,
+                                         marker_obj,
+                                         sort_key,
+                                         sort_dir,
+                                         filters=filters)
+        return HostCollection.convert_with_links(nodes, limit,
+                                                 url=resource_url,
+                                                 expand=expand,
+                                                 sort_key=sort_key,
+                                                 sort_dir=sort_dir)
diff --git a/zun/api/controllers/v1/views/hosts_view.py b/zun/api/controllers/v1/views/hosts_view.py
new file mode 100644
index 000000000..36032bd40
--- /dev/null
+++ b/zun/api/controllers/v1/views/hosts_view.py
@@ -0,0 +1,45 @@
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import itertools
+
+from zun.api.controllers import link
+
+
+_basic_keys = (
+    'uuid',
+    'hostname',
+    'mem_total',
+    'cpus',
+    'os',
+    'labels'
+)
+
+
+def format_host(url, host):
+    def transform(key, value):
+        if key not in _basic_keys:
+            return
+        if key == 'uuid':
+            yield ('uuid', value)
+            yield ('links', [link.make_link(
+                'self', url, 'hosts', value),
+                link.make_link(
+                    'bookmark', url,
+                    'hosts', value,
+                    bookmark=True)])
+        else:
+            yield (key, value)
+
+    return dict(itertools.chain.from_iterable(
+        transform(k, v) for k, v in host.as_dict().items()))
diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py
index 3d34ccdca..4f06a7c37 100644
--- a/zun/api/controllers/versions.py
+++ b/zun/api/controllers/versions.py
@@ -36,10 +36,11 @@ REST_API_VERSION_HISTORY = """REST API Version History:
     * 1.1 - Initial version
     * 1.2 - Support user specify pre created networks
     * 1.3 - Add auto_remove to container
+    * 1.4 - Support list all container host
 """
 
 BASE_VER = '1.1'
-CURRENT_MAX_VER = '1.3'
+CURRENT_MAX_VER = '1.4'
 
 
 class Version(object):
diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst
index 87a23630b..045ddcc75 100644
--- a/zun/api/rest_api_version_history.rst
+++ b/zun/api/rest_api_version_history.rst
@@ -42,3 +42,9 @@ user documentation.
   Add 'auto_remove' field for creating a container.
   With this field, the container will be automatically removed if it exists.
   The new one will be created instead.
+
+1.4
+---
+
+  Add host list api.
+  Users can use this api to list all the zun compute hosts.
diff --git a/zun/tests/unit/api/controllers/test_root.py b/zun/tests/unit/api/controllers/test_root.py
index fbcc4230f..ef260a7c5 100644
--- a/zun/tests/unit/api/controllers/test_root.py
+++ b/zun/tests/unit/api/controllers/test_root.py
@@ -25,7 +25,7 @@ class TestRootController(api_base.FunctionalTest):
             u'default_version':
             {u'id': u'v1',
              u'links': [{u'href': u'http://localhost/v1/', u'rel': u'self'}],
-             u'max_version': u'1.3',
+             u'max_version': u'1.4',
              u'min_version': u'1.1',
              u'status': u'CURRENT'},
             u'description': u'Zun is an OpenStack project which '
@@ -33,7 +33,7 @@ class TestRootController(api_base.FunctionalTest):
             u'versions': [{u'id': u'v1',
                            u'links': [{u'href': u'http://localhost/v1/',
                                        u'rel': u'self'}],
-                           u'max_version': u'1.3',
+                           u'max_version': u'1.4',
                            u'min_version': u'1.1',
                            u'status': u'CURRENT'}]}
 
@@ -56,6 +56,10 @@ class TestRootController(api_base.FunctionalTest):
                              u'rel': u'self'},
                             {u'href': u'http://localhost/containers/',
                              u'rel': u'bookmark'}],
+            u'hosts': [{u'href': u'http://localhost/v1/hosts/',
+                        u'rel': u'self'},
+                       {u'href': u'http://localhost/hosts/',
+                        u'rel': u'bookmark'}],
             u'images': [{u'href': u'http://localhost/v1/images/',
                          u'rel': u'self'},
                         {u'href': u'http://localhost/images/',
diff --git a/zun/tests/unit/api/controllers/v1/test_hosts.py b/zun/tests/unit/api/controllers/v1/test_hosts.py
new file mode 100644
index 000000000..68963b780
--- /dev/null
+++ b/zun/tests/unit/api/controllers/v1/test_hosts.py
@@ -0,0 +1,89 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+import mock
+from mock import patch
+
+from oslo_utils import uuidutils
+
+from zun import objects
+from zun.objects import numa
+from zun.tests.unit.api import base as api_base
+from zun.tests.unit.db import utils
+
+
+class TestHostController(api_base.FunctionalTest):
+
+    @patch('zun.objects.ComputeNode.list')
+    def test_get_all_hosts(self, mock_host_list):
+        test_host = utils.get_test_compute_node()
+        numat = numa.NUMATopology._from_dict(test_host['numa_topology'])
+        test_host['numa_topology'] = numat
+        hosts = [objects.ComputeNode(self.context, **test_host)]
+        mock_host_list.return_value = hosts
+
+        extra_environ = {'HTTP_ACCEPT': 'application/json'}
+        headers = {'OpenStack-API-Version': 'container 1.3'}
+        response = self.app.get('/v1/hosts/', extra_environ=extra_environ,
+                                headers=headers)
+
+        mock_host_list.assert_called_once_with(mock.ANY,
+                                               1000, None, 'hostname', 'asc',
+                                               filters=None)
+        self.assertEqual(200, response.status_int)
+        actual_hosts = response.json['hosts']
+        self.assertEqual(1, len(actual_hosts))
+        self.assertEqual(test_host['uuid'],
+                         actual_hosts[0].get('uuid'))
+
+    @patch('zun.objects.ComputeNode.list')
+    def test_get_all_hosts_with_pagination_marker(self, mock_host_list):
+        host_list = []
+        for id_ in range(4):
+            test_host = utils.create_test_compute_node(
+                context=self.context,
+                uuid=uuidutils.generate_uuid())
+            numat = numa.NUMATopology._from_dict(test_host['numa_topology'])
+            test_host['numa_topology'] = numat
+            host = objects.ComputeNode(self.context, **test_host)
+            host_list.append(host)
+        mock_host_list.return_value = host_list[-1:]
+        extra_environ = {'HTTP_ACCEPT': 'application/json'}
+        headers = {'OpenStack-API-Version': 'container 1.3'}
+        response = self.app.get('/v1/hosts/?limit=3&marker=%s'
+                                % host_list[2].uuid,
+                                extra_environ=extra_environ, headers=headers)
+
+        self.assertEqual(200, response.status_int)
+        actual_hosts = response.json['hosts']
+        self.assertEqual(1, len(actual_hosts))
+        self.assertEqual(host_list[-1].uuid,
+                         actual_hosts[0].get('uuid'))
+
+
+class TestHostEnforcement(api_base.FunctionalTest):
+
+    def _common_policy_check(self, rule, func, *arg, **kwarg):
+        self.policy.set_rules({rule: 'project_id:non_fake'})
+        response = func(*arg, **kwarg)
+        self.assertEqual(403, response.status_int)
+        self.assertEqual('application/json', response.content_type)
+        self.assertTrue(
+            "Policy doesn't allow %s to be performed." % rule,
+            response.json['errors'][0]['detail'])
+
+    def test_policy_disallow_get_all(self):
+        extra_environ = {'HTTP_ACCEPT': 'application/json'}
+        headers = {'OpenStack-API-Version': 'container 1.3'}
+        self._common_policy_check(
+            'host:get_all', self.get_json, '/hosts/',
+            expect_errors=True, extra_environ=extra_environ, headers=headers)