Skip to content

Commit 04e2d46

Browse files
Menneni, Shreyamboglesby
authored andcommitted
Pull request #13: Feature/credential manager
Merge in SIE-BB/netapp-dataops-toolkit from feature/credential-manager to release-v2.7.0 * commit 'b5b013544f8015395f08c7be0def9195028378e2': using keyring_service_name from constants.py constants module as a source of truth replacing .env approach by module-level constants keyring module in setup.cfg remove .env file adding default service name while creating config default service_name for keyring, and user instructions replacing print statements with logger adding keyring to dependencies modifying base name .env for single source of truth remove decoding password using keyring to set and get ontap credentials
2 parents 7746bea + b5b0135 commit 04e2d46

5 files changed

Lines changed: 49 additions & 11 deletions

File tree

netapp_dataops_traditional/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ netapp_dataops_cli.py config
4242

4343
Note: The toolkit requires an ONTAP account with API access. The toolkit will use this account to access the ONTAP API. NetApp recommends using an SVM-level account. Usage of a cluster admin account should be avoided for security reasons.
4444

45+
> **Note: Keyring Service Configuration**
46+
>
47+
> The toolkit stores ONTAP credentials securely using your system's keyring service. By default, it uses the service name `netapp:dataops:ontap` to store and retrieve credentials.
48+
>
49+
> If you need to use a different service name, you can override this by setting the `KEYRING_SERVICE_NAME` environment variable:
50+
>
51+
> ```sh
52+
> export KEYRING_SERVICE_NAME=my-custom-service-name
53+
> ```
54+
4555
#### Example Usage
4656
4757
```sh
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Constants used across the NetApp DataOps Toolkit."""
2+
3+
# Keyring configuration constants
4+
KEYRING_SERVICE_NAME = "netapp:dataops:ontap"

netapp_dataops_traditional/netapp_dataops/netapp_dataops_cli.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import sys
1010
sys.path.insert(0, "/root/netapp-dataops-toolkit/netapp_dataops_traditional/netapp_dataops")
1111

12+
import keyring
1213
from netapp_dataops import traditional
1314
from netapp_dataops.traditional import (
1415
clone_volume,
@@ -44,10 +45,10 @@
4445
)
4546

4647
from netapp_dataops.logging_utils import setup_logger
48+
from netapp_dataops.constants import KEYRING_SERVICE_NAME
4749

4850
logger = setup_logger(__name__)
4951

50-
5152
## Define contents of help text
5253
helpTextStandard = '''
5354
The NetApp DataOps Toolkit is a Python library that makes it simple for data scientists and data engineers to perform various data management tasks, such as provisioning a new data volume, near-instantaneously cloning a data volume, and near-instantaneously snapshotting a data volume for traceability/baselining.
@@ -634,14 +635,15 @@ def createConfig(configDirPath: str = "~/.netapp_dataops", configFilename: str =
634635

635636
# Prompt user to enter additional config details
636637
config["defaultAggregate"] = input("Enter aggregate to use by default when creating new FlexVol volumes: ")
637-
config["username"] = input("Enter ONTAP API username (Recommendation: Use SVM account): ")
638+
username = input("Enter ONTAP API username (Recommendation: Use SVM account): ")
638639
passwordString = getpass("Enter ONTAP API password (Recommendation: Use SVM account): ")
639640

640-
# Convert password to base64 enconding
641-
passwordBytes = passwordString.encode("ascii")
642-
passwordBase64Bytes = base64.b64encode(passwordBytes)
643-
config["password"] = passwordBase64Bytes.decode("ascii")
644-
641+
# Store the password securely using keyring
642+
if username is not None:
643+
keyring.set_password(KEYRING_SERVICE_NAME, "username", username)
644+
if passwordString is not None:
645+
keyring.set_password(KEYRING_SERVICE_NAME, "password", passwordString)
646+
645647
# Prompt user to enter value denoting whether or not to verify SSL cert when calling ONTAP API
646648
# Verify value entered; prompt user to re-enter if invalid
647649
while True:

netapp_dataops_traditional/netapp_dataops/traditional/__init__.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from concurrent.futures import ThreadPoolExecutor
1818
import boto3
1919
from botocore.client import Config as BotoConfig
20+
import keyring
2021
from netapp_ontap import config as netappConfig
2122
from netapp_ontap.error import NetAppRestError
2223
from netapp_ontap.host_connection import HostConnection as NetAppHostConnection
@@ -32,6 +33,7 @@
3233
import requests
3334
from tabulate import tabulate
3435
import yaml
36+
from netapp_dataops.constants import KEYRING_SERVICE_NAME
3537

3638
from netapp_dataops.logging_utils import setup_logger
3739

@@ -201,17 +203,17 @@ def _instantiate_connection(config: dict, connectionType: str = "ONTAP", print_o
201203
try:
202204
ontapClusterMgmtHostname = config["hostname"]
203205
ontapClusterAdminUsername = config["username"]
204-
ontapClusterAdminPasswordBase64 = config["password"]
206+
ontapClusterAdminPassword = config["password"]
205207
verifySSLCert = config["verifySSLCert"]
206208
except:
207209
if print_output:
208210
_print_invalid_config_error()
209211
raise InvalidConfigError()
210212

211213
# Decode base64-encoded password
212-
ontapClusterAdminPasswordBase64Bytes = ontapClusterAdminPasswordBase64.encode("ascii")
213-
ontapClusterAdminPasswordBytes = base64.b64decode(ontapClusterAdminPasswordBase64Bytes)
214-
ontapClusterAdminPassword = ontapClusterAdminPasswordBytes.decode("ascii")
214+
# ontapClusterAdminPasswordBase64Bytes = ontapClusterAdminPasswordBase64.encode("ascii")
215+
# ontapClusterAdminPasswordBytes = base64.b64decode(ontapClusterAdminPasswordBase64Bytes)
216+
# ontapClusterAdminPassword = ontapClusterAdminPasswordBytes.decode("ascii")
215217

216218
# Instantiate connection to ONTAP cluster
217219
netappConfig.CONNECTION = NetAppHostConnection(
@@ -253,6 +255,25 @@ def _retrieve_config(configDirPath: str = "~/.netapp_dataops", configFilename: s
253255
with open(configFilePath, 'r') as configFile:
254256
# Read connection details from config file; read into dict
255257
config = json.load(configFile)
258+
259+
# Retrieve username and password from os-default credential manager
260+
username = keyring.get_password(KEYRING_SERVICE_NAME, "username")
261+
password = keyring.get_password(KEYRING_SERVICE_NAME, "password")
262+
263+
if username:
264+
config["username"] = username
265+
else:
266+
if print_output:
267+
logger.error("Error: Missing username in credential manager.")
268+
raise InvalidConfigError()
269+
270+
if password:
271+
config["password"] = password
272+
else:
273+
if print_output:
274+
logger.error("Error: Missing password in credential manager.")
275+
raise InvalidConfigError()
276+
256277
except:
257278
if print_output:
258279
_print_invalid_config_error()

netapp_dataops_traditional/setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ install_requires =
4242
boto3
4343
pyyaml
4444
fastmcp
45+
keyring
4546
python_requires = >=3.8,<3.14
4647

4748
[options.extras_require]

0 commit comments

Comments
 (0)