Merge "Update Datera's Authentication method"
This commit is contained in:
commit
35b1a03a45
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2014 Datera
|
||||
# Copyright 2015 Datera
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -37,6 +37,8 @@ class DateraVolumeTestCase(test.TestCase):
|
|||
self.cfg.datera_api_port = '7717'
|
||||
self.cfg.datera_api_version = '1'
|
||||
self.cfg.datera_num_replicas = '2'
|
||||
self.cfg.san_login = 'user'
|
||||
self.cfg.san_password = 'pass'
|
||||
|
||||
mock_exec = mock.Mock()
|
||||
mock_exec.return_value = ('', '')
|
||||
|
@ -244,6 +246,17 @@ class DateraVolumeTestCase(test.TestCase):
|
|||
self.assertRaises(exception.DateraAPIException,
|
||||
self.driver.extend_volume, volume, 2)
|
||||
|
||||
def test_login_successful(self):
|
||||
self.mock_api.return_value = {
|
||||
'key': 'dd2469de081346c28ac100e071709403'
|
||||
}
|
||||
self.assertIsNone(self.driver._login())
|
||||
self.assertEqual(1, self.mock_api.call_count)
|
||||
|
||||
def test_login_unsuccessful(self):
|
||||
self.mock_api.side_effect = exception.NotAuthorized
|
||||
self.assertRaises(exception.NotAuthorized, self.driver._login)
|
||||
self.assertEqual(1, self.mock_api.call_count)
|
||||
|
||||
stub_export = {
|
||||
u'_ipColl': [u'172.28.121.10', u'172.28.120.10'],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2014 Datera
|
||||
# Copyright 2015 Datera
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -16,12 +16,14 @@
|
|||
import json
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import units
|
||||
import requests
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE
|
||||
from cinder.i18n import _, _LE, _LW
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.openstack.common import versionutils
|
||||
from cinder.volume.drivers.san import san
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -29,7 +31,9 @@ LOG = logging.getLogger(__name__)
|
|||
d_opts = [
|
||||
cfg.StrOpt('datera_api_token',
|
||||
default=None,
|
||||
help='Datera API token.'),
|
||||
help='DEPRECATED: This will be removed in the Liberty release. '
|
||||
'Use san_login and san_password instead. This directly '
|
||||
'sets the Datera API token.'),
|
||||
cfg.StrOpt('datera_api_port',
|
||||
default='7717',
|
||||
help='Datera API port.'),
|
||||
|
@ -45,9 +49,32 @@ d_opts = [
|
|||
CONF = cfg.CONF
|
||||
CONF.import_opt('driver_client_cert_key', 'cinder.volume.driver')
|
||||
CONF.import_opt('driver_client_cert', 'cinder.volume.driver')
|
||||
CONF.import_opt('driver_use_ssl', 'cinder.volume.driver')
|
||||
CONF.register_opts(d_opts)
|
||||
|
||||
|
||||
def _authenticated(func):
|
||||
"""Ensure the driver is authenticated to make a request.
|
||||
|
||||
In do_setup() we fetch an auth token and store it. If that expires when
|
||||
we do API request, we'll fetch a new one.
|
||||
"""
|
||||
def func_wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
except exception.NotAuthorized:
|
||||
# Prevent recursion loop. After the self arg is the
|
||||
# resource_type arg from _issue_api_request(). If attempt to
|
||||
# login failed, we should just give up.
|
||||
if args[0] == 'login':
|
||||
raise
|
||||
|
||||
# Token might've expired, get a new one, try again.
|
||||
self._login()
|
||||
return func(self, *args, **kwargs)
|
||||
return func_wrapper
|
||||
|
||||
|
||||
class DateraDriver(san.SanISCSIDriver):
|
||||
"""The OpenStack Datera Driver
|
||||
|
||||
|
@ -60,8 +87,39 @@ class DateraDriver(san.SanISCSIDriver):
|
|||
super(DateraDriver, self).__init__(*args, **kwargs)
|
||||
self.configuration.append_config_values(d_opts)
|
||||
self.num_replicas = self.configuration.datera_num_replicas
|
||||
self.username = self.configuration.san_login
|
||||
self.password = self.configuration.san_password
|
||||
self.auth_token = None
|
||||
self.cluster_stats = {}
|
||||
|
||||
def do_setup(self, context):
|
||||
# If any of the deprecated options are set, we'll warn the operator to
|
||||
# use the new authentication method.
|
||||
DEPRECATED_OPTS = [
|
||||
self.configuration.driver_client_cert_key,
|
||||
self.configuration.driver_client_cert,
|
||||
self.configuration.datera_api_token
|
||||
]
|
||||
|
||||
if any(DEPRECATED_OPTS):
|
||||
msg = _LW("Client cert verification and datera_api_token are "
|
||||
"deprecated in the Datera driver, and will be removed "
|
||||
"in the Liberty release. Please set the san_login and "
|
||||
"san_password in your cinder.conf instead.")
|
||||
versionutils.report_deprecated_feature(LOG, msg)
|
||||
return
|
||||
|
||||
# If we can't authenticate through the old and new method, just fail
|
||||
# now.
|
||||
if not all([self.username, self.password]):
|
||||
msg = _("san_login and/or san_password is not set for Datera "
|
||||
"driver in the cinder.conf. Set this information and "
|
||||
"start the cinder-volume service again.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(msg)
|
||||
|
||||
self._login()
|
||||
|
||||
def create_volume(self, volume):
|
||||
"""Create a logical volume."""
|
||||
params = {
|
||||
|
@ -193,8 +251,28 @@ class DateraDriver(san.SanISCSIDriver):
|
|||
|
||||
self.cluster_stats = stats
|
||||
|
||||
def _login(self):
|
||||
"""Use the san_login and san_password to set self.auth_token."""
|
||||
data = {
|
||||
'name': self.username,
|
||||
'password': self.password
|
||||
}
|
||||
|
||||
try:
|
||||
LOG.debug('Getting Datera auth token.')
|
||||
results = self._issue_api_request('login', 'post', body=data,
|
||||
sensitive=True)
|
||||
self.auth_token = results['key']
|
||||
except exception.NotAuthorized:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Logging into the Datera cluster failed. Please '
|
||||
'check your username and password set in the '
|
||||
'cinder.conf and start the cinder-volume'
|
||||
'service again.'))
|
||||
|
||||
@_authenticated
|
||||
def _issue_api_request(self, resource_type, method='get', resource=None,
|
||||
body=None, action=None):
|
||||
body=None, action=None, sensitive=False):
|
||||
"""All API requests to Datera cluster go through this method.
|
||||
|
||||
:param resource_type: the type of the resource
|
||||
|
@ -211,16 +289,26 @@ class DateraDriver(san.SanISCSIDriver):
|
|||
|
||||
payload = json.dumps(body, ensure_ascii=False)
|
||||
payload.encode('utf-8')
|
||||
header = {'Content-Type': 'application/json; charset=utf-8'}
|
||||
|
||||
if not sensitive:
|
||||
LOG.debug("Payload for Datera API call: %s", payload)
|
||||
|
||||
header = {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'auth-token': self.auth_token
|
||||
}
|
||||
|
||||
protocol = 'http'
|
||||
if self.configuration.driver_use_ssl:
|
||||
protocol = 'https'
|
||||
|
||||
# TODO(thingee): Auth method through Auth-Token is deprecated. Remove
|
||||
# this and client cert verification stuff in the Liberty release.
|
||||
if api_token:
|
||||
header['Auth-Token'] = api_token
|
||||
|
||||
LOG.debug("Payload for Datera API call: %s", payload)
|
||||
|
||||
client_cert = self.configuration.driver_client_cert
|
||||
client_cert_key = self.configuration.driver_client_cert_key
|
||||
protocol = 'http'
|
||||
cert_data = None
|
||||
|
||||
if client_cert:
|
||||
|
@ -247,10 +335,14 @@ class DateraDriver(san.SanISCSIDriver):
|
|||
raise exception.DateraAPIException(msg)
|
||||
|
||||
data = response.json()
|
||||
LOG.debug("Results of Datera API call: %s", data)
|
||||
if not sensitive:
|
||||
LOG.debug("Results of Datera API call: %s", data)
|
||||
|
||||
if not response.ok:
|
||||
if response.status_code == 404:
|
||||
raise exception.NotFound(data['message'])
|
||||
elif response.status_code in [403, 401]:
|
||||
raise exception.NotAuthorized()
|
||||
else:
|
||||
msg = _('Request to Datera cluster returned bad status:'
|
||||
' %(status)s | %(reason)s') % {
|
||||
|
|
Loading…
Reference in New Issue