Makes novaclient use the volumes endpoint
* Depends on https://review.openstack.org/#change,4479 * Adds support to change service type including tests * Adds decorator for methods that need to use another service type * Changes volume and snapshots to use the volume endpoint * These extensions will move into the volume client once it exists * Fixes bug 940017 Change-Id: I683e4ca6c67e278d8aa8a9acec3dc0f1872f43f2
This commit is contained in:
parent
39bfd0585b
commit
03f54c57e1
@ -36,7 +36,8 @@ class HTTPClient(httplib2.Http):
|
||||
|
||||
def __init__(self, user, password, projectid, auth_url, insecure=False,
|
||||
timeout=None, token=None, region_name=None,
|
||||
endpoint_type='publicURL', service_name=None):
|
||||
endpoint_type='publicURL', service_type=None,
|
||||
service_name=None):
|
||||
super(HTTPClient, self).__init__(timeout=timeout)
|
||||
self.user = user
|
||||
self.password = password
|
||||
@ -45,6 +46,7 @@ class HTTPClient(httplib2.Http):
|
||||
self.version = 'v1.1'
|
||||
self.region_name = region_name
|
||||
self.endpoint_type = endpoint_type
|
||||
self.service_type = service_type
|
||||
self.service_name = service_name
|
||||
|
||||
self.management_url = None
|
||||
@ -158,6 +160,7 @@ class HTTPClient(httplib2.Http):
|
||||
attr='region',
|
||||
filter_value=self.region_name,
|
||||
endpoint_type=self.endpoint_type,
|
||||
service_type=self.service_type,
|
||||
service_name=self.service_name)
|
||||
self.management_url = management_url.rstrip('/')
|
||||
return None
|
||||
|
@ -29,7 +29,7 @@ class ServiceCatalog(object):
|
||||
return self.catalog['access']['token']['id']
|
||||
|
||||
def url_for(self, attr=None, filter_value=None,
|
||||
service_type='compute', endpoint_type='publicURL',
|
||||
service_type=None, endpoint_type='publicURL',
|
||||
service_name=None):
|
||||
"""Fetch the public URL from the Compute service for
|
||||
a particular endpoint attribute. If none given, return
|
||||
|
@ -36,6 +36,7 @@ from novaclient.v1_1 import shell as shell_v1_1
|
||||
|
||||
DEFAULT_NOVA_VERSION = "1.1"
|
||||
DEFAULT_NOVA_ENDPOINT_TYPE = 'publicURL'
|
||||
DEFAULT_NOVA_SERVICE_TYPE = 'compute'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -102,6 +103,9 @@ class OpenStackComputeShell(object):
|
||||
default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'),
|
||||
help='Defaults to env[OS_REGION_NAME].')
|
||||
|
||||
parser.add_argument('--service_type',
|
||||
help='Defaults to compute for most actions')
|
||||
|
||||
parser.add_argument('--service_name',
|
||||
default=utils.env('NOVA_SERVICE_NAME'),
|
||||
help='Defaults to env[NOVA_SERVICE_NAME]')
|
||||
@ -267,15 +271,20 @@ class OpenStackComputeShell(object):
|
||||
return 0
|
||||
|
||||
(user, apikey, password, projectid, tenant_name, url, auth_url,
|
||||
region_name, endpoint_type, insecure, service_name) = (
|
||||
region_name, endpoint_type, insecure, service_type,
|
||||
service_name) = (
|
||||
args.username, args.apikey, args.password,
|
||||
args.projectid, args.tenant_name, args.url,
|
||||
args.auth_url, args.region_name, args.endpoint_type,
|
||||
args.insecure, args.service_name)
|
||||
args.insecure, args.service_type, args.service_name)
|
||||
|
||||
if not endpoint_type:
|
||||
endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE
|
||||
|
||||
if not service_type:
|
||||
service_type = DEFAULT_NOVA_SERVICE_TYPE
|
||||
service_type = utils.get_service_type(args.func) or service_type
|
||||
|
||||
#FIXME(usrleon): Here should be restrict for project id same as
|
||||
# for username or password but for compatibility it is not.
|
||||
|
||||
@ -319,6 +328,7 @@ class OpenStackComputeShell(object):
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
extensions=self.extensions,
|
||||
service_type=service_type,
|
||||
service_name=service_name)
|
||||
|
||||
try:
|
||||
|
@ -96,6 +96,27 @@ def isunauthenticated(f):
|
||||
return getattr(f, 'unauthenticated', False)
|
||||
|
||||
|
||||
def service_type(stype):
|
||||
"""
|
||||
Adds 'service_type' attribute to decorated function.
|
||||
Usage:
|
||||
@service_type('volume')
|
||||
def mymethod(f):
|
||||
...
|
||||
"""
|
||||
def inner(f):
|
||||
f.service_type = stype
|
||||
return f
|
||||
return inner
|
||||
|
||||
|
||||
def get_service_type(f):
|
||||
"""
|
||||
Retrieves service type from function
|
||||
"""
|
||||
return getattr(f, 'service_type', None)
|
||||
|
||||
|
||||
def pretty_choice_list(l):
|
||||
return ', '.join("'%s'" % i for i in l)
|
||||
|
||||
|
@ -40,7 +40,7 @@ class Client(object):
|
||||
def __init__(self, username, api_key, project_id, auth_url,
|
||||
insecure=False, timeout=None, token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_name=None):
|
||||
service_type=None, service_name=None):
|
||||
# FIXME(comstud): Rename the api_key argument above when we
|
||||
# know it's not being used as keyword argument
|
||||
password = api_key
|
||||
@ -84,6 +84,7 @@ class Client(object):
|
||||
token=token,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name)
|
||||
|
||||
def authenticate(self):
|
||||
|
@ -740,6 +740,7 @@ def _translate_volume_snapshot_keys(collection):
|
||||
setattr(item, to_key, item._info[from_key])
|
||||
|
||||
|
||||
@utils.service_type('volume')
|
||||
def do_volume_list(cs, args):
|
||||
"""List all the volumes."""
|
||||
volumes = cs.volumes.list()
|
||||
@ -754,6 +755,7 @@ def do_volume_list(cs, args):
|
||||
|
||||
|
||||
@utils.arg('volume', metavar='<volume>', help='ID of the volume.')
|
||||
@utils.service_type('volume')
|
||||
def do_volume_show(cs, args):
|
||||
"""Show details about a volume."""
|
||||
volume = _find_volume(cs, args.volume)
|
||||
@ -774,6 +776,7 @@ def do_volume_show(cs, args):
|
||||
@utils.arg('--display_description', metavar='<display_description>',
|
||||
help='Optional volume description. (Default=None)',
|
||||
default=None)
|
||||
@utils.service_type('volume')
|
||||
def do_volume_create(cs, args):
|
||||
"""Add a new volume."""
|
||||
cs.volumes.create(args.size,
|
||||
@ -783,6 +786,7 @@ def do_volume_create(cs, args):
|
||||
|
||||
|
||||
@utils.arg('volume', metavar='<volume>', help='ID of the volume to delete.')
|
||||
@utils.service_type('volume')
|
||||
def do_volume_delete(cs, args):
|
||||
"""Remove a volume."""
|
||||
volume = _find_volume(cs, args.volume)
|
||||
@ -818,6 +822,7 @@ def do_volume_detach(cs, args):
|
||||
args.attachment_id)
|
||||
|
||||
|
||||
@utils.service_type('volume')
|
||||
def do_volume_snapshot_list(cs, args):
|
||||
"""List all the snapshots."""
|
||||
snapshots = cs.volume_snapshots.list()
|
||||
@ -827,6 +832,7 @@ def do_volume_snapshot_list(cs, args):
|
||||
|
||||
|
||||
@utils.arg('snapshot', metavar='<snapshot>', help='ID of the snapshot.')
|
||||
@utils.service_type('volume')
|
||||
def do_volume_snapshot_show(cs, args):
|
||||
"""Show details about a snapshot."""
|
||||
snapshot = _find_volume_snapshot(cs, args.snapshot)
|
||||
@ -848,6 +854,7 @@ def do_volume_snapshot_show(cs, args):
|
||||
@utils.arg('--display_description', metavar='<display_description>',
|
||||
help='Optional snapshot description. (Default=None)',
|
||||
default=None)
|
||||
@utils.service_type('volume')
|
||||
def do_volume_snapshot_create(cs, args):
|
||||
"""Add a new snapshot."""
|
||||
cs.volume_snapshots.create(args.volume_id,
|
||||
@ -859,6 +866,7 @@ def do_volume_snapshot_create(cs, args):
|
||||
@utils.arg('snapshot_id',
|
||||
metavar='<snapshot_id>',
|
||||
help='ID of the snapshot to delete.')
|
||||
@utils.service_type('volume')
|
||||
def do_volume_snapshot_delete(cs, args):
|
||||
"""Remove a snapshot."""
|
||||
snapshot = _find_volume_snapshot(cs, args.snapshot_id)
|
||||
|
@ -57,7 +57,7 @@ class SnapshotManager(base.ManagerWithFind):
|
||||
'force': force,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description}}
|
||||
return self._create('/os-snapshots', body, 'snapshot')
|
||||
return self._create('/snapshots', body, 'snapshot')
|
||||
|
||||
def get(self, snapshot_id):
|
||||
"""
|
||||
@ -66,7 +66,7 @@ class SnapshotManager(base.ManagerWithFind):
|
||||
:param snapshot_id: The ID of the snapshot to get.
|
||||
:rtype: :class:`Snapshot`
|
||||
"""
|
||||
return self._get("/os-snapshots/%s" % snapshot_id, "snapshot")
|
||||
return self._get("/snapshots/%s" % snapshot_id, "snapshot")
|
||||
|
||||
def list(self, detailed=True):
|
||||
"""
|
||||
@ -75,9 +75,9 @@ class SnapshotManager(base.ManagerWithFind):
|
||||
:rtype: list of :class:`Snapshot`
|
||||
"""
|
||||
if detailed is True:
|
||||
return self._list("/os-snapshots/detail", "snapshots")
|
||||
return self._list("/snapshots/detail", "snapshots")
|
||||
else:
|
||||
return self._list("/os-snapshots", "snapshots")
|
||||
return self._list("/snapshots", "snapshots")
|
||||
|
||||
def delete(self, snapshot):
|
||||
"""
|
||||
@ -85,4 +85,4 @@ class SnapshotManager(base.ManagerWithFind):
|
||||
|
||||
:param snapshot: The :class:`Snapshot` to delete.
|
||||
"""
|
||||
self._delete("/os-snapshots/%s" % base.getid(snapshot))
|
||||
self._delete("/snapshots/%s" % base.getid(snapshot))
|
||||
|
@ -55,7 +55,7 @@ class VolumeManager(base.ManagerWithFind):
|
||||
'snapshot_id': snapshot_id,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description}}
|
||||
return self._create('/os-volumes', body, 'volume')
|
||||
return self._create('/volumes', body, 'volume')
|
||||
|
||||
def get(self, volume_id):
|
||||
"""
|
||||
@ -64,7 +64,7 @@ class VolumeManager(base.ManagerWithFind):
|
||||
:param volume_id: The ID of the volume to delete.
|
||||
:rtype: :class:`Volume`
|
||||
"""
|
||||
return self._get("/os-volumes/%s" % volume_id, "volume")
|
||||
return self._get("/volumes/%s" % volume_id, "volume")
|
||||
|
||||
def list(self, detailed=True):
|
||||
"""
|
||||
@ -73,9 +73,9 @@ class VolumeManager(base.ManagerWithFind):
|
||||
:rtype: list of :class:`Volume`
|
||||
"""
|
||||
if detailed is True:
|
||||
return self._list("/os-volumes/detail", "volumes")
|
||||
return self._list("/volumes/detail", "volumes")
|
||||
else:
|
||||
return self._list("/os-volumes", "volumes")
|
||||
return self._list("/volumes", "volumes")
|
||||
|
||||
def delete(self, volume):
|
||||
"""
|
||||
@ -83,7 +83,7 @@ class VolumeManager(base.ManagerWithFind):
|
||||
|
||||
:param volume: The :class:`Volume` to delete.
|
||||
"""
|
||||
self._delete("/os-volumes/%s" % base.getid(volume))
|
||||
self._delete("/volumes/%s" % base.getid(volume))
|
||||
|
||||
def create_server_volume(self, server_id, volume_id, device):
|
||||
"""
|
||||
|
@ -59,13 +59,13 @@ SERVICE_CATALOG = {
|
||||
"endpoints_links": [],
|
||||
},
|
||||
{
|
||||
"name": "Cloud Files",
|
||||
"type": "object-store",
|
||||
"name": "Nova Volumes",
|
||||
"type": "volume",
|
||||
"endpoints": [
|
||||
{
|
||||
"tenantId": "11",
|
||||
"publicURL": "https://compute1.host/v1/blah-blah",
|
||||
"internalURL": "https://compute1.host/v1/blah-blah",
|
||||
"tenantId": "1",
|
||||
"publicURL": "https://volume1.host/v1/1234",
|
||||
"internalURL": "https://volume1.host/v1/1234",
|
||||
"region": "South",
|
||||
"versionId": "1.0",
|
||||
"versionInfo": "uri",
|
||||
@ -73,12 +73,12 @@ SERVICE_CATALOG = {
|
||||
},
|
||||
{
|
||||
"tenantId": "2",
|
||||
"publicURL": "https://compute1.host/v1.1/blah-blah",
|
||||
"internalURL": "https://compute1.host/v1.1/blah-blah",
|
||||
"publicURL": "https://volume1.host/v1.1/3456",
|
||||
"internalURL": "https://volume1.host/v1.1/3456",
|
||||
"region": "South",
|
||||
"versionId": "1.1",
|
||||
"versionInfo": "https://compute1.host/v1.1/",
|
||||
"versionList": "https://compute1.host/"
|
||||
"versionInfo": "https://volume1.host/v1.1/",
|
||||
"versionList": "https://volume1.host/"
|
||||
},
|
||||
],
|
||||
"endpoints_links": [
|
||||
@ -103,11 +103,25 @@ class ServiceCatalogTest(utils.TestCase):
|
||||
def test_building_a_service_catalog(self):
|
||||
sc = service_catalog.ServiceCatalog(SERVICE_CATALOG)
|
||||
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for)
|
||||
self.assertEquals(sc.url_for('tenantId', '1'),
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
service_type='compute')
|
||||
self.assertEquals(sc.url_for('tenantId', '1', service_type='compute'),
|
||||
"https://compute1.host/v1/1234")
|
||||
self.assertEquals(sc.url_for('tenantId', '2'),
|
||||
self.assertEquals(sc.url_for('tenantId', '2', service_type='compute'),
|
||||
"https://compute1.host/v1.1/3456")
|
||||
|
||||
self.assertRaises(exceptions.EndpointNotFound,
|
||||
sc.url_for, "region", "South")
|
||||
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
|
||||
"region", "South", service_type='compute')
|
||||
|
||||
def test_alternate_service_type(self):
|
||||
sc = service_catalog.ServiceCatalog(SERVICE_CATALOG)
|
||||
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
service_type='volume')
|
||||
self.assertEquals(sc.url_for('tenantId', '1', service_type='volume'),
|
||||
"https://volume1.host/v1/1234")
|
||||
self.assertEquals(sc.url_for('tenantId', '2', service_type='volume'),
|
||||
"https://volume1.host/v1.1/3456")
|
||||
|
||||
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
|
||||
"region", "North", service_type='volume')
|
||||
|
@ -18,7 +18,7 @@ def to_http_response(resp_dict):
|
||||
class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
def test_authenticate_success(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0")
|
||||
"auth_url/v2.0", service_type='compute')
|
||||
resp = {
|
||||
"access": {
|
||||
"token": {
|
||||
@ -99,7 +99,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v1.0")
|
||||
"auth_url/v1.0", service_type='compute')
|
||||
dict_correct_response = {
|
||||
"access": {
|
||||
"token": {
|
||||
@ -180,7 +180,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_ambiguous_endpoints(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0")
|
||||
"auth_url/v2.0", service_type='compute')
|
||||
resp = {
|
||||
"access": {
|
||||
"token": {
|
||||
|
Loading…
Reference in New Issue
Block a user