diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py
index 18dc30588..430d76c1a 100644
--- a/cinderclient/tests/v2/fakes.py
+++ b/cinderclient/tests/v2/fakes.py
@@ -95,6 +95,13 @@ def _stub_cgsnapshot(**kwargs):
     return cgsnapshot
 
 
+def _stub_type_access(**kwargs):
+    access = {'volume_type_id': '11111111-1111-1111-1111-111111111111',
+              'project_id': '00000000-0000-0000-0000-000000000000'}
+    access.update(kwargs)
+    return access
+
+
 def _self_href(base_uri, tenant_id, backup_id):
     return '%s/v2/%s/backups/%s' % (base_uri, tenant_id, backup_id)
 
@@ -559,25 +566,37 @@ class FakeHTTPClient(base_client.HTTPClient):
     #
     # VolumeTypes
     #
+
     def get_types(self, **kw):
         return (200, {}, {
             'volume_types': [{'id': 1,
                               'name': 'test-type-1',
+                              'description': 'test_type-1-desc',
                               'extra_specs': {}},
                              {'id': 2,
                               'name': 'test-type-2',
+                              'description': 'test_type-2-desc',
                               'extra_specs': {}}]})
 
     def get_types_1(self, **kw):
         return (200, {}, {'volume_type': {'id': 1,
                           'name': 'test-type-1',
+                          'description': 'test_type-1-desc',
                           'extra_specs': {}}})
 
     def get_types_2(self, **kw):
         return (200, {}, {'volume_type': {'id': 2,
                           'name': 'test-type-2',
+                          'description': 'test_type-2-desc',
                           'extra_specs': {}}})
 
+    def get_types_3(self, **kw):
+        return (200, {}, {'volume_type': {'id': 3,
+                          'name': 'test-type-3',
+                          'description': 'test_type-3-desc',
+                          'extra_specs': {},
+                          'os-volume-type-access:is_public': False}})
+
     def get_types_default(self, **kw):
         return self.get_types_1()
 
@@ -587,6 +606,19 @@ class FakeHTTPClient(base_client.HTTPClient):
                           'description': 'test_type-3-desc',
                           'extra_specs': {}}})
 
+    def post_types_3_action(self, body, **kw):
+        _body = None
+        resp = 202
+        assert len(list(body)) == 1
+        action = list(body)[0]
+        if action == 'addProjectAccess':
+            assert 'project' in body['addProjectAccess']
+        elif action == 'removeProjectAccess':
+            assert 'project' in body['removeProjectAccess']
+        else:
+            raise AssertionError('Unexpected action: %s' % action)
+        return (resp, {}, _body)
+
     def post_types_1_extra_specs(self, body, **kw):
         assert list(body) == ['extra_specs']
         return (200, {}, {'extra_specs': {'k': 'v'}})
@@ -600,6 +632,15 @@ class FakeHTTPClient(base_client.HTTPClient):
     def put_types_1(self, **kw):
         return self.get_types_1()
 
+    #
+    # VolumeAccess
+    #
+
+    def get_types_3_os_volume_type_access(self, **kw):
+        return (200, {}, {'volume_type_access': [
+            _stub_type_access()
+        ]})
+
     #
     # VolumeEncryptionTypes
     #
diff --git a/cinderclient/tests/v2/test_shell.py b/cinderclient/tests/v2/test_shell.py
index 9aceed6ec..b9ab7a4c2 100644
--- a/cinderclient/tests/v2/test_shell.py
+++ b/cinderclient/tests/v2/test_shell.py
@@ -298,6 +298,55 @@ class ShellTest(utils.TestCase):
         self.assert_called_anytime('POST', '/snapshots/5678/action',
                                    body=expected)
 
+    def test_type_list(self):
+        self.run_command('type-list')
+        self.assert_called_anytime('GET', '/types')
+
+    def test_type_list_all(self):
+        self.run_command('type-list --all')
+        self.assert_called_anytime('GET', '/types?is_public=None')
+
+    def test_type_create(self):
+        self.run_command('type-create test-type-1')
+        self.assert_called('POST', '/types')
+
+    def test_type_create_public(self):
+        expected = {'volume_type': {'name': 'test-type-1',
+                                    'description': 'test_type-1-desc',
+                                    'os-volume-type-access:is_public': True}}
+        self.run_command('type-create test-type-1 '
+                         '--description=test_type-1-desc '
+                         '--is-public=True')
+        self.assert_called('POST', '/types', body=expected)
+
+    def test_type_create_private(self):
+        expected = {'volume_type': {'name': 'test-type-3',
+                                    'description': 'test_type-3-desc',
+                                    'os-volume-type-access:is_public': False}}
+        self.run_command('type-create test-type-3 '
+                         '--description=test_type-3-desc '
+                         '--is-public=False')
+        self.assert_called('POST', '/types', body=expected)
+
+    def test_type_access_list(self):
+        self.run_command('type-access-list --volume-type 3')
+        self.assert_called('GET', '/types/3/os-volume-type-access')
+
+    def test_type_access_add_project(self):
+        expected = {'addProjectAccess': {'project': '101'}}
+        self.run_command('type-access-add --volume-type 3 --project-id 101')
+        self.assert_called_anytime('GET', '/types/3')
+        self.assert_called('POST', '/types/3/action',
+                           body=expected)
+
+    def test_type_access_remove_project(self):
+        expected = {'removeProjectAccess': {'project': '101'}}
+        self.run_command('type-access-remove '
+                         '--volume-type 3 --project-id 101')
+        self.assert_called_anytime('GET', '/types/3')
+        self.assert_called('POST', '/types/3/action',
+                           body=expected)
+
     def test_encryption_type_list(self):
         """
         Test encryption-type-list shell command.
diff --git a/cinderclient/tests/v2/test_type_access.py b/cinderclient/tests/v2/test_type_access.py
new file mode 100644
index 000000000..bf9a6b282
--- /dev/null
+++ b/cinderclient/tests/v2/test_type_access.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2013 OpenStack Foundation
+#
+# All Rights Reserved.
+#
+#    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 cinderclient.v2 import volume_type_access
+from cinderclient.tests import utils
+from cinderclient.tests.v2 import fakes
+
+cs = fakes.FakeClient()
+
+PROJECT_UUID = '11111111-1111-1111-111111111111'
+
+
+class TypeAccessTest(utils.TestCase):
+
+    def test_list(self):
+        access = cs.volume_type_access.list(volume_type='3')
+        cs.assert_called('GET', '/types/3/os-volume-type-access')
+        for a in access:
+            self.assertTrue(isinstance(a, volume_type_access.VolumeTypeAccess))
+
+    def test_add_project_access(self):
+        cs.volume_type_access.add_project_access('3', PROJECT_UUID)
+        cs.assert_called('POST', '/types/3/action',
+                         {'addProjectAccess': {'project': PROJECT_UUID}})
+
+    def test_remove_project_access(self):
+        cs.volume_type_access.remove_project_access('3', PROJECT_UUID)
+        cs.assert_called('POST', '/types/3/action',
+                         {'removeProjectAccess': {'project': PROJECT_UUID}})
diff --git a/cinderclient/tests/v2/test_types.py b/cinderclient/tests/v2/test_types.py
index efe027ff3..108cbbab3 100644
--- a/cinderclient/tests/v2/test_types.py
+++ b/cinderclient/tests/v2/test_types.py
@@ -22,15 +22,35 @@ cs = fakes.FakeClient()
 
 
 class TypesTest(utils.TestCase):
+
     def test_list_types(self):
         tl = cs.volume_types.list()
         cs.assert_called('GET', '/types')
         for t in tl:
             self.assertIsInstance(t, volume_types.VolumeType)
 
+    def test_list_types_not_public(self):
+        cs.volume_types.list(is_public=None)
+        cs.assert_called('GET', '/types?is_public=None')
+
     def test_create(self):
-        t = cs.volume_types.create('test-type-3')
-        cs.assert_called('POST', '/types')
+        t = cs.volume_types.create('test-type-3', 'test-type-3-desc')
+        cs.assert_called('POST', '/types',
+                         {'volume_type': {
+                          'name': 'test-type-3',
+                          'description': 'test-type-3-desc',
+                          'os-volume-type-access:is_public': True
+                          }})
+        self.assertIsInstance(t, volume_types.VolumeType)
+
+    def test_create_non_public(self):
+        t = cs.volume_types.create('test-type-3', 'test-type-3-desc', False)
+        cs.assert_called('POST', '/types',
+                         {'volume_type': {
+                          'name': 'test-type-3',
+                          'description': 'test-type-3-desc',
+                          'os-volume-type-access:is_public': False
+                          }})
         self.assertIsInstance(t, volume_types.VolumeType)
 
     def test_update(self):
diff --git a/cinderclient/v2/client.py b/cinderclient/v2/client.py
index 88d51ceec..4619ea270 100644
--- a/cinderclient/v2/client.py
+++ b/cinderclient/v2/client.py
@@ -25,6 +25,7 @@ from cinderclient.v2 import services
 from cinderclient.v2 import volumes
 from cinderclient.v2 import volume_snapshots
 from cinderclient.v2 import volume_types
+from cinderclient.v2 import volume_type_access
 from cinderclient.v2 import volume_encryption_types
 from cinderclient.v2 import volume_backups
 from cinderclient.v2 import volume_backups_restore
@@ -61,6 +62,8 @@ class Client(object):
         self.volumes = volumes.VolumeManager(self)
         self.volume_snapshots = volume_snapshots.SnapshotManager(self)
         self.volume_types = volume_types.VolumeTypeManager(self)
+        self.volume_type_access = \
+            volume_type_access.VolumeTypeAccessManager(self)
         self.volume_encryption_types = \
             volume_encryption_types.VolumeEncryptionTypeManager(self)
         self.qos_specs = qos_specs.QoSSpecsManager(self)
diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py
index a0f697746..c61ba4a33 100644
--- a/cinderclient/v2/shell.py
+++ b/cinderclient/v2/shell.py
@@ -696,13 +696,21 @@ def do_snapshot_reset_state(cs, args):
 
 
 def _print_volume_type_list(vtypes):
-    utils.print_list(vtypes, ['ID', 'Name', 'Description'])
+    utils.print_list(vtypes, ['ID', 'Name', 'Description', 'Is_Public'])
 
 
 @utils.service_type('volumev2')
+@utils.arg('--all',
+           dest='all',
+           action='store_true',
+           default=False,
+           help='Display all volume types (Admin only).')
 def do_type_list(cs, args):
     """Lists available 'volume types'."""
-    vtypes = cs.volume_types.list()
+    if args.all:
+        vtypes = cs.volume_types.list(is_public=None)
+    else:
+        vtypes = cs.volume_types.list()
     _print_volume_type_list(vtypes)
 
 
@@ -739,10 +747,15 @@ def do_extra_specs_list(cs, args):
 @utils.arg('--description',
            metavar='<description>',
            help="Description of new volume type.")
+@utils.arg('--is-public',
+           metavar='<is-public>',
+           help="Make type accessible to the public (default true).",
+           default=True)
 @utils.service_type('volumev2')
 def do_type_create(cs, args):
     """Creates a volume type."""
-    vtype = cs.volume_types.create(args.name, args.description)
+    is_public = strutils.bool_from_string(args.is_public)
+    vtype = cs.volume_types.create(args.name, args.description, is_public)
     _print_volume_type_list([vtype])
 
 
@@ -780,6 +793,45 @@ def do_type_key(cs, args):
         vtype.unset_keys(list(keypair))
 
 
+@utils.arg('--volume-type', metavar='<volume_type>', required=True,
+           help="Filter results by volume type name or ID.")
+@utils.service_type('volumev2')
+def do_type_access_list(cs, args):
+    """Print access information about the given volume type."""
+    volume_type = _find_volume_type(cs, args.volume_type)
+    if volume_type.is_public:
+        raise exceptions.CommandError("Failed to get access list "
+                                      "for public volume type.")
+    access_list = cs.volume_type_access.list(volume_type)
+
+    columns = ['Volume_type_ID', 'Project_ID']
+    utils.print_list(access_list, columns)
+
+
+@utils.arg('--volume-type', metavar='<volume_type>', required=True,
+           help="Volume type name or ID to add access for the given project.")
+@utils.arg('--project-id', metavar='<project_id>', required=True,
+           help='Project ID to add volume type access for.')
+@utils.service_type('volumev2')
+def do_type_access_add(cs, args):
+    """Adds volume type access for the given project."""
+    vtype = _find_volume_type(cs, args.volume_type)
+    cs.volume_type_access.add_project_access(vtype, args.project_id)
+
+
+@utils.arg('--volume-type', metavar='<volume_type>', required=True,
+           help=('Volume type name or ID to remove access '
+                 'for the given project.'))
+@utils.arg('--project-id', metavar='<project_id>', required=True,
+           help='Project ID to remove volume type access for.')
+@utils.service_type('volumev2')
+def do_type_access_remove(cs, args):
+    """Removes volume type access for the given project."""
+    vtype = _find_volume_type(cs, args.volume_type)
+    cs.volume_type_access.remove_project_access(
+        vtype, args.project_id)
+
+
 @utils.service_type('volumev2')
 def do_endpoints(cs, args):
     """Discovers endpoints registered by authentication service."""
diff --git a/cinderclient/v2/volume_type_access.py b/cinderclient/v2/volume_type_access.py
new file mode 100644
index 000000000..d604041a5
--- /dev/null
+++ b/cinderclient/v2/volume_type_access.py
@@ -0,0 +1,51 @@
+# Copyright 2014 OpenStack Foundation
+#
+#    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.
+
+"""Volume type access interface."""
+
+from cinderclient import base
+
+
+class VolumeTypeAccess(base.Resource):
+    def __repr__(self):
+        return "<VolumeTypeAccess: %s>" % self.project_id
+
+
+class VolumeTypeAccessManager(base.ManagerWithFind):
+    """
+    Manage :class:`VolumeTypeAccess` resources.
+    """
+    resource_class = VolumeTypeAccess
+
+    def list(self, volume_type):
+        return self._list(
+            '/types/%s/os-volume-type-access' % base.getid(volume_type),
+            'volume_type_access')
+
+    def add_project_access(self, volume_type, project):
+        """Add a project to the given volume type access list."""
+        info = {'project': project}
+        self._action('addProjectAccess', volume_type, info)
+
+    def remove_project_access(self, volume_type, project):
+        """Remove a project from the given volume type access list."""
+        info = {'project': project}
+        self._action('removeProjectAccess', volume_type, info)
+
+    def _action(self, action, volume_type, info, **kwargs):
+        """Perform a volume type action."""
+        body = {action: info}
+        self.run_hooks('modify_body_for_action', body, **kwargs)
+        url = '/types/%s/action' % base.getid(volume_type)
+        return self.api.client.post(url, body=body)
diff --git a/cinderclient/v2/volume_types.py b/cinderclient/v2/volume_types.py
index 6b167d0fb..34eb7ebe2 100644
--- a/cinderclient/v2/volume_types.py
+++ b/cinderclient/v2/volume_types.py
@@ -24,6 +24,13 @@ class VolumeType(base.Resource):
     def __repr__(self):
         return "<VolumeType: %s>" % self.name
 
+    @property
+    def is_public(self):
+        """
+        Provide a user-friendly accessor to os-volume-type-access:is_public
+        """
+        return self._info.get("os-volume-type-access:is_public", 'N/A')
+
     def get_keys(self):
         """Get extra specs from a volume type.
 
@@ -70,12 +77,15 @@ class VolumeTypeManager(base.ManagerWithFind):
     """Manage :class:`VolumeType` resources."""
     resource_class = VolumeType
 
-    def list(self, search_opts=None):
+    def list(self, search_opts=None, is_public=True):
         """Lists all volume types.
 
         :rtype: list of :class:`VolumeType`.
         """
-        return self._list("/types", "volume_types")
+        query_string = ''
+        if not is_public:
+            query_string = '?is_public=%s' % is_public
+        return self._list("/types%s" % (query_string), "volume_types")
 
     def get(self, volume_type):
         """Get a specific volume type.
@@ -99,18 +109,20 @@ class VolumeTypeManager(base.ManagerWithFind):
         """
         self._delete("/types/%s" % base.getid(volume_type))
 
-    def create(self, name, description=None):
+    def create(self, name, description=None, is_public=True):
         """Creates a volume type.
 
         :param name: Descriptive name of the volume type
         :param description: Description of the the volume type
+        :param is_public: Volume type visibility
         :rtype: :class:`VolumeType`
         """
 
         body = {
             "volume_type": {
                 "name": name,
-                "description": description
+                "description": description,
+                "os-volume-type-access:is_public": is_public,
             }
         }