Merge "added API token based auth"
This commit is contained in:
@@ -69,6 +69,7 @@ other parameters that are not specific to VAST Share Driver.
|
||||
vast_mgmt_port = {vms_port}
|
||||
vast_mgmt_user = {mgmt_user}
|
||||
vast_mgmt_password = {mgmt_password}
|
||||
vast_api_token = {vast_api_token}
|
||||
vast_vippool_name = {vip_pool}
|
||||
vast_root_export = {root_export}
|
||||
|
||||
|
||||
@@ -30,3 +30,5 @@
|
||||
- (String) Username for VAST management API.
|
||||
* - ``vast_mgmt_password`` =
|
||||
- (String) Password for VAST management API.
|
||||
* - ``vast_api_token`` =
|
||||
- (String) API token for accessing VAST mgmt. If provided, it will be used instead of 'vast_mgmt_user' and 'vast_mgmt_password'.
|
||||
|
||||
@@ -79,6 +79,16 @@ OPTS = [
|
||||
help="Password for VAST management",
|
||||
secret=True
|
||||
),
|
||||
cfg.StrOpt(
|
||||
"vast_api_token",
|
||||
default="",
|
||||
secret=True,
|
||||
help=(
|
||||
"API token for accessing VAST mgmt. "
|
||||
"If provided, it will be used instead "
|
||||
"of 'san_login' and 'san_password'."
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@@ -116,19 +126,29 @@ class VASTShareDriver(driver.ShareDriver):
|
||||
|
||||
username = self.configuration.safe_get("vast_mgmt_user")
|
||||
password = self.configuration.safe_get("vast_mgmt_password")
|
||||
api_token = self.configuration.safe_get("vast_api_token")
|
||||
host = self.configuration.safe_get("vast_mgmt_host")
|
||||
port = self.configuration.safe_get("vast_mgmt_port")
|
||||
if not all((username, password, port)):
|
||||
if not host:
|
||||
raise exception.VastDriverException(
|
||||
reason="Not all required parameters are present."
|
||||
" Make sure you specified `vast_mgmt_host`,"
|
||||
" `vast_mgmt_port`, and `vast_mgmt_user` "
|
||||
"in manila.conf."
|
||||
reason="`vast_mgmt_host` must be set in manila.conf."
|
||||
)
|
||||
# Require either (username & password) OR (API token)
|
||||
if not ((username and password) or api_token):
|
||||
raise exception.VastDriverException(
|
||||
reason="Authentication failed: You must specify either "
|
||||
"`vast_mgmt_user` and `vast_mgmt_password`, "
|
||||
"or provide `vast_api_token` in manila.conf."
|
||||
)
|
||||
if port:
|
||||
host = f"{host}:{port}"
|
||||
self.rest = vast_rest.RestApi(
|
||||
host, username, password, False, self.VERSION
|
||||
host=host,
|
||||
username=username,
|
||||
password=password,
|
||||
api_token=api_token,
|
||||
ssl_verify=False,
|
||||
plugin_version=self.VERSION,
|
||||
)
|
||||
LOG.debug("VAST Data driver setup is complete.")
|
||||
|
||||
|
||||
@@ -32,20 +32,37 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class Session(requests.Session):
|
||||
|
||||
def __init__(self, host, username, password, ssl_verify, plugin_version):
|
||||
def __init__(
|
||||
self,
|
||||
host,
|
||||
username,
|
||||
password,
|
||||
api_token,
|
||||
ssl_verify,
|
||||
plugin_version,
|
||||
):
|
||||
super().__init__()
|
||||
self.base_url = f"https://{host.strip('/')}/api"
|
||||
self.ssl_verify = ssl_verify
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.token = api_token
|
||||
self.headers["Accept"] = "application/json"
|
||||
self.headers["Content-Type"] = "application/json"
|
||||
self.headers["User-Agent"] = (
|
||||
f"manila/v{plugin_version}"
|
||||
f" ({requests.utils.default_user_agent()})"
|
||||
)
|
||||
# will be updated on first request
|
||||
self.headers["authorization"] = "Bearer"
|
||||
if self.token:
|
||||
LOG.info("VMS session is using API token authentication.")
|
||||
self.headers["authorization"] = f"Api-Token {self.token}"
|
||||
else:
|
||||
# Will be updated on the first request
|
||||
LOG.info(
|
||||
"VMS session is using username/password authentication"
|
||||
" (Bearer token will be acquired)."
|
||||
)
|
||||
self.headers["authorization"] = "Bearer"
|
||||
|
||||
if not ssl_verify:
|
||||
import urllib3
|
||||
@@ -95,7 +112,8 @@ class Session(requests.Session):
|
||||
ret = super().request(
|
||||
verb, url, verify=self.ssl_verify, params=params, **kwargs
|
||||
)
|
||||
if ret.status_code == 403 and "Token is invalid" in ret.text:
|
||||
# No refresh for token based auth. Token should be long-lived.
|
||||
if ret.status_code == 403 and not self.token:
|
||||
self.refresh_auth_token()
|
||||
raise exception.VastApiRetry(reason="Token is invalid or expired.")
|
||||
|
||||
@@ -307,11 +325,20 @@ class Folders(VastResource):
|
||||
|
||||
class RestApi:
|
||||
|
||||
def __init__(self, host, username, password, ssl_verify, plugin_version):
|
||||
def __init__(
|
||||
self,
|
||||
host,
|
||||
username,
|
||||
password,
|
||||
api_token,
|
||||
ssl_verify,
|
||||
plugin_version,
|
||||
):
|
||||
self.session = Session(
|
||||
host=host,
|
||||
username=username,
|
||||
password=password,
|
||||
api_token=api_token,
|
||||
ssl_verify=ssl_verify,
|
||||
plugin_version=plugin_version,
|
||||
)
|
||||
|
||||
@@ -94,7 +94,7 @@ class VASTShareDriverTestCase(unittest.TestCase):
|
||||
self.assertFalse(session.ssl_verify)
|
||||
self.assertEqual(session.base_url, "https://test:443/api")
|
||||
|
||||
@ddt.data("vast_mgmt_user", "vast_vippool_name")
|
||||
@ddt.data("vast_mgmt_user", "vast_vippool_name", "vast_mgmt_host")
|
||||
def test_do_setup_missing_required_fields(self, missing_field):
|
||||
self.fake_conf.set_default(missing_field, None)
|
||||
_driver = driver.VASTShareDriver(
|
||||
@@ -103,6 +103,16 @@ class VASTShareDriverTestCase(unittest.TestCase):
|
||||
with self.assertRaises(exception.VastDriverException):
|
||||
_driver.do_setup(self._context)
|
||||
|
||||
def test_do_setup_with_api_token(self):
|
||||
self.fake_conf.set_default("vast_mgmt_user", None)
|
||||
self.fake_conf.set_default("vast_mgmt_password", None)
|
||||
self.fake_conf.set_default("vast_api_token", "test_token")
|
||||
_driver = driver.VASTShareDriver(
|
||||
execute=mock.MagicMock(), configuration=self.fake_conf
|
||||
)
|
||||
_driver.do_setup(self._context)
|
||||
self.assertEqual(_driver.rest.session.token, "test_token")
|
||||
|
||||
@mock.patch(
|
||||
"manila.share.drivers.vastdata.rest.Session.get",
|
||||
mock.MagicMock(return_value=fake_metrics),
|
||||
|
||||
@@ -101,8 +101,12 @@ class TestSession(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.session = vast_rest.Session(
|
||||
"host", "username",
|
||||
"password", False, "1.0"
|
||||
"host",
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
False,
|
||||
"1.0",
|
||||
)
|
||||
|
||||
@mock.patch("requests.Session.request")
|
||||
@@ -274,6 +278,7 @@ class ViewTest(unittest.TestCase):
|
||||
"host",
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
True,
|
||||
"1.0"
|
||||
)
|
||||
@@ -324,6 +329,7 @@ class TestCapacityMetrics(unittest.TestCase):
|
||||
"host",
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
True,
|
||||
"1.0"
|
||||
)
|
||||
@@ -344,7 +350,12 @@ class TestFolders(unittest.TestCase):
|
||||
)
|
||||
def setUp(self):
|
||||
self.rest_api = vast_rest.RestApi(
|
||||
"host", "username", "password", True, "1.0"
|
||||
"host",
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
True,
|
||||
"1.0",
|
||||
)
|
||||
|
||||
@ddt.data(
|
||||
@@ -446,6 +457,7 @@ class VipPoolTest(unittest.TestCase):
|
||||
"host",
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
True,
|
||||
"1.0"
|
||||
)
|
||||
@@ -506,7 +518,12 @@ class TestRestApi(unittest.TestCase):
|
||||
mock.MagicMock(sys_version="1.0")
|
||||
]
|
||||
rest_api = vast_rest.RestApi(
|
||||
"host", "username", "password", True, "1.0"
|
||||
"host",
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
True,
|
||||
"1.0",
|
||||
)
|
||||
version = rest_api.get_sw_version()
|
||||
self.assertEqual(version, "1.0")
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- Added support for authentication using an API token in the VAST Manila driver.
|
||||
Introduced the `vast_api_token` configuration option, allowing users to
|
||||
authenticate with a pre-generated API token instead of using `vast_mgmt_user`
|
||||
and `vast_mgmt_password`.
|
||||
Reference in New Issue
Block a user