Skip to content

Commit 62a95f7

Browse files
sushma-m1mboglesby
authored andcommitted
Pull request #16: Feature/gcnv traditional
Merge in SIE-BB/netapp-dataops-toolkit from feature/gcnv-traditional to release-v2.7.0 * commit 'f5022c330e6442304251d762bdc5735e2d80c4be': fixing output format for delete volume and delete snapshot Updates: - moving gcnv mcp server to mcp_server folder - adding path to scripts in setup.cfg adding mcp to scripts in setup config including necessary changes for adding logging module adding logging_utils.py to the correct path copied logging_utils.py from feature/code-refactoring/logger
2 parents 302146a + f5022c3 commit 62a95f7

8 files changed

Lines changed: 228 additions & 99 deletions

File tree

netapp_dataops_traditional/docs/gcnv_readme.md

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ def create_volume(
210210
large_capacity: bool = None, # Flag indicating if the volume will be a large capacity volume or a regular volume. If set to True, the volume will be a large capacity volume.
211211
multiple_endpoints: bool = None, # Flag indicating if the volume will have an IP address per node for volumes supporting multiple IP endpoints. Only the volume with large_capacity will be allowed to have multiple endpoints.
212212
tiering_enabled: bool = None, # Flag indicating if the volume has tiering policy enable/pause.
213-
cooling_threshold_days: int = None # Time in days to mark the volume's data block as cold and make it eligible for tiering. It can be range from 2-183.
214-
):
213+
cooling_threshold_days: int = None, # Time in days to mark the volume's data block as cold and make it eligible for tiering. It can be range from 2-183.
214+
print_output: bool = False # print log to the console
215+
) -> Dict[str, Any]:
215216
```
216217

217218
#### Return Values
@@ -301,8 +302,9 @@ def clone_volume(
301302
large_capacity: bool = None, # Flag indicating if the volume will be a large capacity volume or a regular volume. If set to True, the volume will be a large capacity volume.
302303
multiple_endpoints: bool = None, # Flag indicating if the volume will have an IP address per node for volumes supporting multiple IP endpoints. Only the volume with large_capacity will be allowed to have multiple endpoints.
303304
tiering_enabled: bool = None, # Flag indicating if the volume has tiering policy enable/pause.
304-
cooling_threshold_days: int = None # Time in days to mark the volume's data block as cold and make it eligible for tiering. It can be range from 2-183.
305-
):
305+
cooling_threshold_days: int = None, # Time in days to mark the volume's data block as cold and make it eligible for tiering. It can be range from 2-183.
306+
print_output: bool = False # print log to the console
307+
) -> Dict[str, Any]:
306308
```
307309

308310
#### Return Values
@@ -353,10 +355,12 @@ Volumes can be deleted, with options for forced deletion if necessary.
353355
#### Function Definition
354356
```python
355357
def delete_volume(
356-
project_id: str, # Required. The ID of the project.
357-
location: str, # Required. The location of the volume.
358-
volume_id: str, # Required. The ID of the volume to delete.
359-
force: bool = False): # If set to True, the volume will be deleted even if it is not empty.
358+
project_id: str, # Required. The ID of the project.
359+
location: str, # Required. The location of the volume.
360+
volume_id: str, # Required. The ID of the volume to delete.
361+
force: bool = False, # If set to True, the volume will be deleted even if it is not empty.
362+
print_output: bool = False # print log to the console
363+
) -> Dict[str, Any]:
360364
```
361365
#### Return Values
362366
```
@@ -399,8 +403,12 @@ Retrieve all volumes in a project/location.
399403
#### Function Definition
400404
```python
401405
from netapp_dataops.traditional import gcnv
402-
project_id: str, # Required. The ID of the project.
403-
location: str): # Required. The location to list volumes from.
406+
407+
def list_volumes(
408+
project_id: str, # Required. The ID of the project.
409+
location: str, # Required. The location to list volumes from.
410+
print_output: bool = False # print log to the console
411+
) -> Dict[str, Any]:
404412
```
405413
#### Return Values
406414
```
@@ -451,8 +459,9 @@ def create_snapshot(
451459
volume_id: str, # Required. The ID of the volume to delete.
452460
snapshot_id: str, # Required. The ID of the snapshot to create.
453461
description: str = None, # The description of the snapshot.
454-
labels: dict = None # The labels to assign to the snapshot.
455-
):
462+
labels: dict = None, # The labels to assign to the snapshot.
463+
print_output: bool = False # print log to the console
464+
) -> Dict[str, Any]:
456465
```
457466
#### Return Values
458467
```
@@ -500,8 +509,9 @@ def delete_snapshot(
500509
project_id: str, # Required. The ID of the project.
501510
location: str, # Required. The location of the volume.
502511
volume_id: str, # Required. The ID of the volume.
503-
snapshot_id: str # Required. The ID of the snapshot to delete.
504-
):
512+
snapshot_id: str, # Required. The ID of the snapshot to delete.
513+
print_output: bool = False # print log to the console
514+
) -> Dict[str, Any]:
505515
```
506516

507517
#### Return Values
@@ -546,10 +556,11 @@ Users can enumerate all snapshots associated with a particular volume.
546556
#### Function Definition
547557
```python
548558
def list_snapshots(
549-
project_id: str, # Required. The ID of the project.
550-
location: str, # Required. The location to list volumes from.
551-
volume_id: str # Required. The ID of the volume to list snapshots for.
552-
):
559+
project_id: str, # Required. The ID of the project.
560+
location: str, # Required. The location to list volumes from.
561+
volume_id: str, # Required. The ID of the volume to list snapshots for.
562+
print_output: bool = False # print log to the console
563+
) -> Dict[str, Any]:
553564
```
554565

555566
#### Return Values
@@ -608,8 +619,9 @@ def create_replication(
608619
tiering_enabled: bool = None, # Whether tiering is enabled on the destination volume.
609620
cooling_threshold_days: int = None, # Time in days to mark the volume's data block as cold and make it eligible for tiering. It can be range from 2-183.
610621
description: str = None, # A description about this replication relationship.
611-
labels: dict = None # Resource labels to represent user provided metadata
612-
):
622+
labels: dict = None, # Resource labels to represent user provided metadata
623+
print_output: bool = False # print log to the console
624+
) -> Dict[str, Any]:
613625
```
614626

615627
#### Return Values
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import logging
2+
3+
def setup_logger(name: str, level: int = logging.DEBUG) -> logging.Logger:
4+
"""
5+
Sets up a logger with a console handler.
6+
7+
Args:
8+
name (str): The name of the logger.
9+
level (int): The logging level. Default is logging.DEBUG.
10+
11+
Returns:
12+
logging.Logger: Configured logger.
13+
"""
14+
logger = logging.getLogger(name)
15+
logger.setLevel(level)
16+
if not logger.hasHandlers():
17+
console_handler = logging.StreamHandler()
18+
console_handler.setLevel(level)
19+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
20+
console_handler.setFormatter(formatter)
21+
logger.addHandler(console_handler)
22+
return logger

netapp_dataops_traditional/netapp_dataops/netapp_dataops_gcnv_mcp.py renamed to netapp_dataops_traditional/netapp_dataops/mcp_server/netapp_dataops_gcnv_mcp.py

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
#!/usr/bin/env python3
2-
import sys
32
import asyncio
4-
import json
5-
import logging
63
from typing import Any, Dict, Optional
74
from fastmcp import FastMCP
85
from google.protobuf.json_format import MessageToDict
96

10-
# Configure logging
11-
logging.basicConfig(level=logging.INFO)
12-
logger = logging.getLogger(__name__)
7+
from netapp_dataops.logging_utils import setup_logger
8+
9+
logger = setup_logger(__name__)
1310

1411
# Importing the necessary functions from the traditional GCNV module
1512
from netapp_dataops.traditional.gcnv import (
@@ -50,7 +47,8 @@ async def create_volume_tool(
5047
large_capacity: Optional[bool] = None,
5148
multiple_endpoints: Optional[bool] = None,
5249
tiering_enabled: Optional[bool] = None,
53-
cooling_threshold_days: Optional[int] = None
50+
cooling_threshold_days: Optional[int] = None,
51+
print_output: bool = False
5452
) -> Dict[str, Any]:
5553
"""
5654
Use this tool to create a new volume in the specified project and location.
@@ -144,6 +142,9 @@ async def create_volume_tool(
144142
Optional. Time in days to mark the volume's data block as cold and make it eligible for tiering.
145143
It can be range from 2-183.
146144
Defaults to 31.
145+
print_output (bool):
146+
Optional. If set to True, prints log messages to the console.
147+
Defaults to False.
147148
148149
Returns:
149150
dict: Dictionary with keys
@@ -180,10 +181,12 @@ async def create_volume_tool(
180181
large_capacity=large_capacity,
181182
multiple_endpoints=multiple_endpoints,
182183
tiering_enabled=tiering_enabled,
183-
cooling_threshold_days=cooling_threshold_days
184+
cooling_threshold_days=cooling_threshold_days,
185+
print_output=print_output
184186
)
185187
if response['status'] == 'error':
186-
logger.error(f"Error creating volume: {response['message']}")
188+
if print_output:
189+
logger.error(f"Error creating volume: {response['message']}")
187190
return response
188191

189192

@@ -214,7 +217,8 @@ async def clone_volume_tool(
214217
large_capacity: Optional[bool] = None,
215218
multiple_endpoints: Optional[bool] = None,
216219
tiering_enabled: Optional[bool] = None,
217-
cooling_threshold_days: Optional[int] = None
220+
cooling_threshold_days: Optional[int] = None,
221+
print_output: bool = False
218222
) -> Dict[str, Any]:
219223
"""
220224
Use this tool to clone an existing volume.
@@ -308,6 +312,9 @@ async def clone_volume_tool(
308312
Optional. Time in days to mark the volume's data block as cold and make it eligible for tiering.
309313
It can be range from 2-183.
310314
Defaults to 31.
315+
print_output (bool):
316+
Optional. If set to True, prints log messages to the console.
317+
Defaults to False.
311318
312319
Returns:
313320
dict: Dictionary with keys
@@ -345,16 +352,19 @@ async def clone_volume_tool(
345352
large_capacity=large_capacity,
346353
multiple_endpoints=multiple_endpoints,
347354
tiering_enabled=tiering_enabled,
348-
cooling_threshold_days=cooling_threshold_days
355+
cooling_threshold_days=cooling_threshold_days,
356+
print_output=print_output
349357
)
350358
if response['status'] == 'error':
351-
logger.error(f"Error cloning volume: {response.get('message', 'Unknown error')}")
359+
if print_output:
360+
logger.error(f"Error cloning volume: {response.get('message', 'Unknown error')}")
352361
return response
353362

354363
@mcp.tool(name = "List Volumes")
355364
async def list_volumes_tool(
356365
project_id: str,
357-
location: str
366+
location: str,
367+
print_output: bool = False
358368
) -> Dict[str, Any]:
359369
"""
360370
Use this tool to list all volumes in a project and location.
@@ -364,6 +374,9 @@ async def list_volumes_tool(
364374
Required. The ID of the project.
365375
location (str):
366376
Required. The location to list volumes from.
377+
print_output (bool):
378+
Optional. If set to True, prints log messages to the console.
379+
Defaults to False.
367380
368381
Returns:
369382
dict: Dictionary with keys
@@ -377,10 +390,12 @@ async def list_volumes_tool(
377390
"""
378391
response = list_volumes(
379392
project_id=project_id,
380-
location=location
393+
location=location,
394+
print_output=print_output
381395
)
382396
if response['status'] == 'error':
383-
logger.error(f"Error listing volumes: {response['message']}")
397+
if print_output:
398+
logger.error(f"Error listing volumes: {response['message']}")
384399
return response
385400

386401
@mcp.tool(name = "Create Snapshot")
@@ -390,7 +405,8 @@ async def create_snapshot_tool(
390405
volume_id: str,
391406
snapshot_id: str,
392407
description: Optional[str] = None,
393-
labels: Optional[dict] = None
408+
labels: Optional[dict] = None,
409+
print_output: bool = False
394410
) -> Dict[str, Any]:
395411
"""
396412
Use this tool to create a near-instantaneous, space-efficient, read-only copy of an existing data volume, called a snapshot.
@@ -410,6 +426,9 @@ async def create_snapshot_tool(
410426
Optional. The description of the snapshot. Defaults to None.
411427
labels (dict, optional):
412428
Optional. The labels to assign to the snapshot. Defaults to None.
429+
print_output (bool):
430+
Optional. If set to True, prints log messages to the console.
431+
Defaults to False.
413432
414433
Returns:
415434
dict: Dictionary with keys
@@ -427,17 +446,20 @@ async def create_snapshot_tool(
427446
volume_id=volume_id,
428447
snapshot_id=snapshot_id,
429448
description=description,
430-
labels=labels
449+
labels=labels,
450+
print_output=print_output
431451
)
432452
if response['status'] == 'error':
433-
logger.error(f"Error creating snapshot: {response['message']}")
453+
if print_output:
454+
logger.error(f"Error creating snapshot: {response['message']}")
434455
return response
435456

436457
@mcp.tool(name = "List Snapshots")
437458
async def list_snapshots_tool(
438459
project_id: str,
439460
location: str,
440-
volume_id: str
461+
volume_id: str,
462+
print_output: bool = False
441463
) -> Dict[str, Any]:
442464
"""
443465
Use this tool to list all snapshots for a given volume.
@@ -449,6 +471,9 @@ async def list_snapshots_tool(
449471
Required. The location to list volumes from.
450472
volume_id (str):
451473
Required. The ID of the volume to list snapshots for.
474+
print_output (bool):
475+
Optional. If set to True, prints log messages to the console.
476+
Defaults to False.
452477
453478
Returns:
454479
dict: Dictionary with keys
@@ -463,10 +488,12 @@ async def list_snapshots_tool(
463488
response = list_snapshots(
464489
project_id=project_id,
465490
location=location,
466-
volume_id=volume_id
491+
volume_id=volume_id,
492+
print_output=print_output
467493
)
468494
if response['status'] == 'error':
469-
logger.error(f"Error listing snapshots: {response['message']}")
495+
if print_output:
496+
logger.error(f"Error listing snapshots: {response['message']}")
470497
return response
471498

472499
@mcp.tool(name = "Create Replication")
@@ -483,7 +510,8 @@ async def create_replication_tool(
483510
tiering_enabled: Optional[bool] = None,
484511
cooling_threshold_days: Optional[int] = None,
485512
description: Optional[str] = None,
486-
labels: Optional[dict] = None
513+
labels: Optional[dict] = None,
514+
print_output: bool = False
487515
) -> Dict[str, Any]:
488516
"""
489517
Use this tool to create a replication for a volume.
@@ -565,6 +593,9 @@ async def create_replication_tool(
565593
labels (dict, MutableMapping[str, str]):
566594
Optional. Resource labels to represent user provided
567595
metadata.
596+
print_output (bool):
597+
Optional. If set to True, prints log messages to the console.
598+
Defaults to False.
568599
569600
Returns:
570601
dict: Dictionary with keys
@@ -589,10 +620,12 @@ async def create_replication_tool(
589620
tiering_enabled=tiering_enabled,
590621
cooling_threshold_days=cooling_threshold_days,
591622
description=description,
592-
labels=labels
623+
labels=labels,
624+
print_output=print_output
593625
)
594626
if response['status'] == 'error':
595-
logger.error(f"Error creating replication: {response['message']}")
627+
if print_output:
628+
logger.error(f"Error creating replication: {response['message']}")
596629
return response
597630

598631
# Register the MCP instance to run the tools

netapp_dataops_traditional/netapp_dataops/traditional/gcnv/base.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from google.cloud import netapp_v1
22
from google.protobuf.json_format import MessageToDict
33
from typing import Dict, List, Any, Union
4-
import logging
54

6-
logger = logging.getLogger(__name__)
5+
from netapp_dataops.logging_utils import setup_logger
76

7+
logger = setup_logger(__name__)
88

99
def _serialize(details) -> Union[Dict[str, Any], List[Any], str, int, float, bool, None]:
1010
"""Internal helper to convert protobuf objects (and lists) to JSON-serializable structures."""
@@ -18,9 +18,14 @@ def _serialize(details) -> Union[Dict[str, Any], List[Any], str, int, float, boo
1818
return str(details)
1919

2020

21-
def create_client() -> netapp_v1.NetAppClient:
21+
def create_client(print_output: bool = False) -> netapp_v1.NetAppClient:
2222
"""Create and return a NetApp client.
2323
24+
Args:
25+
print_output (bool):
26+
Optional. If set to True, prints log messages to the console.
27+
Defaults to False.
28+
2429
Returns:
2530
netapp_v1.NetAppClient: The NetApp client instance.
2631
@@ -30,7 +35,8 @@ def create_client() -> netapp_v1.NetAppClient:
3035
try:
3136
return netapp_v1.NetAppClient()
3237
except Exception as e:
33-
logger.error(f"An error occurred while creating the NetApp client: {e}")
38+
if print_output:
39+
logger.error(f"An error occurred while creating the NetApp client: {e}")
3440
raise e
3541

3642

0 commit comments

Comments
 (0)