Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down
2 changes: 2 additions & 0 deletions doc/source/configuration/tables/manila-vastdata.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
32 changes: 26 additions & 6 deletions manila/share/drivers/vastdata/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@
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
Expand Down Expand Up @@ -116,19 +126,29 @@ def do_setup(self, context):

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="`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="Not all required parameters are present."
" Make sure you specified `vast_mgmt_host`,"
" `vast_mgmt_port`, and `vast_mgmt_user` "
"in manila.conf."
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.")

Expand Down
37 changes: 32 additions & 5 deletions manila/share/drivers/vastdata/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,37 @@

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
Expand Down Expand Up @@ -95,7 +112,8 @@ def request(
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.")

Expand Down Expand Up @@ -307,11 +325,20 @@ def delete(self, path):

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,
)
Expand Down
12 changes: 11 additions & 1 deletion manila/tests/share/drivers/vastdata/test_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_do_setup(self):
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(
Expand All @@ -103,6 +103,16 @@ def test_do_setup_missing_required_fields(self, missing_field):
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),
Expand Down
25 changes: 21 additions & 4 deletions manila/tests/share/drivers/vastdata/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -274,6 +278,7 @@ def test_view_create(self):
"host",
"username",
"password",
"",
True,
"1.0"
)
Expand Down Expand Up @@ -324,6 +329,7 @@ def test_capacity_metrics(self):
"host",
"username",
"password",
"",
True,
"1.0"
)
Expand All @@ -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(
Expand Down Expand Up @@ -446,6 +457,7 @@ def setUp(self):
"host",
"username",
"password",
"",
True,
"1.0"
)
Expand Down Expand Up @@ -506,7 +518,12 @@ def test_get_sw_version(self, mock_session):
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")
Original file line number Diff line number Diff line change
@@ -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`.
Loading