@gcnv/gcnv-mcp-server 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GEMINI.md +200 -0
- package/LICENSE +201 -0
- package/README.md +374 -0
- package/build/index.js +185 -0
- package/build/logger.js +19 -0
- package/build/registry/register-tools.js +101 -0
- package/build/registry/tool-registry.js +27 -0
- package/build/tools/active-directory-tools.js +124 -0
- package/build/tools/backup-policy-tools.js +140 -0
- package/build/tools/backup-tools.js +178 -0
- package/build/tools/backup-vault-tools.js +147 -0
- package/build/tools/handlers/active-directory-handler.js +321 -0
- package/build/tools/handlers/backup-handler.js +451 -0
- package/build/tools/handlers/backup-policy-handler.js +275 -0
- package/build/tools/handlers/backup-vault-handler.js +370 -0
- package/build/tools/handlers/kms-config-handler.js +327 -0
- package/build/tools/handlers/operation-handler.js +254 -0
- package/build/tools/handlers/quota-rule-handler.js +411 -0
- package/build/tools/handlers/replication-handler.js +504 -0
- package/build/tools/handlers/snapshot-handler.js +320 -0
- package/build/tools/handlers/storage-pool-handler.js +346 -0
- package/build/tools/handlers/volume-handler.js +353 -0
- package/build/tools/kms-config-tools.js +162 -0
- package/build/tools/operation-tools.js +64 -0
- package/build/tools/quota-rule-tools.js +166 -0
- package/build/tools/replication-tools.js +227 -0
- package/build/tools/snapshot-tools.js +124 -0
- package/build/tools/storage-pool-tools.js +215 -0
- package/build/tools/volume-tools.js +216 -0
- package/build/types/tool.js +1 -0
- package/build/utils/netapp-client-factory.js +53 -0
- package/gemini-extension.json +12 -0
- package/package.json +66 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { NetAppClientFactory } from '../../utils/netapp-client-factory.js';
|
|
2
|
+
import { createBackupPolicyTool, deleteBackupPolicyTool, getBackupPolicyTool, listBackupPoliciesTool, updateBackupPolicyTool, } from '../backup-policy-tools.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new backup policy
|
|
5
|
+
*/
|
|
6
|
+
export const createBackupPolicyHandler = async (args) => {
|
|
7
|
+
try {
|
|
8
|
+
const { projectId, location, backupPolicyId, dailyBackupLimit, weeklyBackupLimit, monthlyBackupLimit, description, enabled, labels, } = args;
|
|
9
|
+
const client = NetAppClientFactory.createClient();
|
|
10
|
+
// Format resource name
|
|
11
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
12
|
+
// Build backup policy
|
|
13
|
+
const backupPolicy = {
|
|
14
|
+
dailyBackupLimit,
|
|
15
|
+
weeklyBackupLimit,
|
|
16
|
+
monthlyBackupLimit,
|
|
17
|
+
description,
|
|
18
|
+
enabled,
|
|
19
|
+
labels,
|
|
20
|
+
};
|
|
21
|
+
// Filter out undefined properties
|
|
22
|
+
Object.keys(backupPolicy).forEach((key) => {
|
|
23
|
+
if (backupPolicy[key] === undefined) {
|
|
24
|
+
delete backupPolicy[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
// Call the API to create a backup policy
|
|
28
|
+
const [operation] = await client.createBackupPolicy({
|
|
29
|
+
parent,
|
|
30
|
+
backupPolicy,
|
|
31
|
+
backupPolicyId,
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: 'text',
|
|
37
|
+
text: `Created backup policy: ${backupPolicyId}. Operation ID: ${operation.name}`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
structuredContent: {
|
|
41
|
+
name: `projects/${projectId}/locations/${location}/backupPolicy/${backupPolicyId}`,
|
|
42
|
+
operationId: operation.name || '',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `Failed to create backup policy: ${error.message}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Deletes a backup policy
|
|
60
|
+
*/
|
|
61
|
+
export const deleteBackupPolicyHandler = async (args) => {
|
|
62
|
+
try {
|
|
63
|
+
const { projectId, location, backupPolicyId } = args;
|
|
64
|
+
const client = NetAppClientFactory.createClient();
|
|
65
|
+
// Format resource name
|
|
66
|
+
const name = `projects/${projectId}/locations/${location}/backupPolicies/${backupPolicyId}`;
|
|
67
|
+
// Call the API to delete a backup policy
|
|
68
|
+
const [operation] = await client.deleteBackupPolicy({
|
|
69
|
+
name,
|
|
70
|
+
});
|
|
71
|
+
const result = {
|
|
72
|
+
success: true,
|
|
73
|
+
operationId: operation.name || '',
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: 'text',
|
|
79
|
+
text: `Deleted backup policy ${backupPolicyId}. Operation ID: ${result.operationId}`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
structuredContent: result,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: `Failed to delete backup policy: ${error.message}`,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Gets a backup policy
|
|
99
|
+
*/
|
|
100
|
+
export const getBackupPolicyHandler = async (args) => {
|
|
101
|
+
try {
|
|
102
|
+
const { projectId, location, backupPolicyId } = args;
|
|
103
|
+
const client = NetAppClientFactory.createClient();
|
|
104
|
+
// Format resource name
|
|
105
|
+
const name = `projects/${projectId}/locations/${location}/backupPolicies/${backupPolicyId}`;
|
|
106
|
+
// Call the API to get a backup policy
|
|
107
|
+
const [backupPolicy] = await client.getBackupPolicy({ name });
|
|
108
|
+
const result = {
|
|
109
|
+
name: backupPolicy.name || '',
|
|
110
|
+
backupPolicyId: backupPolicy.name?.split('/').pop() || '',
|
|
111
|
+
dailyBackupLimit: backupPolicy.dailyBackupLimit,
|
|
112
|
+
weeklyBackupLimit: backupPolicy.weeklyBackupLimit,
|
|
113
|
+
monthlyBackupLimit: backupPolicy.monthlyBackupLimit,
|
|
114
|
+
description: backupPolicy.description,
|
|
115
|
+
enabled: backupPolicy.enabled || false,
|
|
116
|
+
assignedVolumeCount: backupPolicy.assignedVolumeCount,
|
|
117
|
+
state: backupPolicy.state || 'UNKNOWN',
|
|
118
|
+
createTime: backupPolicy.createTime
|
|
119
|
+
? new Date(Number(backupPolicy.createTime.seconds || 0) * 1000)
|
|
120
|
+
: new Date(),
|
|
121
|
+
labels: backupPolicy.labels,
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: 'text',
|
|
127
|
+
text: JSON.stringify(result, null, 2),
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
structuredContent: result,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
content: [
|
|
136
|
+
{
|
|
137
|
+
type: 'text',
|
|
138
|
+
text: `Failed to get backup policy: ${error.message}`,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
isError: true,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Lists backup policies
|
|
147
|
+
*/
|
|
148
|
+
export const listBackupPoliciesHandler = async (args) => {
|
|
149
|
+
try {
|
|
150
|
+
const { projectId, location, filter, pageSize, pageToken } = args;
|
|
151
|
+
const client = NetAppClientFactory.createClient();
|
|
152
|
+
// Format resource name
|
|
153
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
154
|
+
// Call the API to list backup policies
|
|
155
|
+
const [response] = await client.listBackupPolicies({
|
|
156
|
+
parent,
|
|
157
|
+
filter,
|
|
158
|
+
pageSize,
|
|
159
|
+
pageToken,
|
|
160
|
+
});
|
|
161
|
+
// Handle API response
|
|
162
|
+
const backupPolicies = Array.isArray(response)
|
|
163
|
+
? response
|
|
164
|
+
: response.backupPolicies || [];
|
|
165
|
+
const nextPageToken = typeof response === 'object' && response !== null
|
|
166
|
+
? response.nextPageToken || ''
|
|
167
|
+
: '';
|
|
168
|
+
const result = {
|
|
169
|
+
backupPolicies: backupPolicies.map((policy) => ({
|
|
170
|
+
name: policy.name || '',
|
|
171
|
+
backupPolicyId: policy.name?.split('/').pop() || '',
|
|
172
|
+
dailyBackupLimit: policy.dailyBackupLimit,
|
|
173
|
+
weeklyBackupLimit: policy.weeklyBackupLimit,
|
|
174
|
+
monthlyBackupLimit: policy.monthlyBackupLimit,
|
|
175
|
+
description: policy.description,
|
|
176
|
+
enabled: policy.enabled || false,
|
|
177
|
+
assignedVolumeCount: policy.assignedVolumeCount,
|
|
178
|
+
state: policy.state || 'UNKNOWN',
|
|
179
|
+
createTime: policy.createTime
|
|
180
|
+
? new Date(Number(policy.createTime.seconds ?? 0) * 1000)
|
|
181
|
+
: undefined,
|
|
182
|
+
labels: policy.labels,
|
|
183
|
+
})),
|
|
184
|
+
nextPageToken: nextPageToken,
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
content: [
|
|
188
|
+
{
|
|
189
|
+
type: 'text',
|
|
190
|
+
text: JSON.stringify({ backupPolicies: backupPolicies, nextPageToken: nextPageToken }, null, 2),
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
structuredContent: result,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: 'text',
|
|
201
|
+
text: `Failed to list backup policies: ${error.message}`,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
isError: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Updates a backup policy
|
|
210
|
+
*/
|
|
211
|
+
export const updateBackupPolicyHandler = async (args) => {
|
|
212
|
+
try {
|
|
213
|
+
const { projectId, location, backupPolicyId, dailyBackupLimit, weeklyBackupLimit, monthlyBackupLimit, description, enabled, labels, } = args;
|
|
214
|
+
const client = NetAppClientFactory.createClient();
|
|
215
|
+
// Format resource name
|
|
216
|
+
const name = `projects/${projectId}/locations/${location}/backupPolicies/${backupPolicyId}`;
|
|
217
|
+
// Build backup policy update
|
|
218
|
+
const backupPolicy = {
|
|
219
|
+
name,
|
|
220
|
+
dailyBackupLimit,
|
|
221
|
+
weeklyBackupLimit,
|
|
222
|
+
monthlyBackupLimit,
|
|
223
|
+
description,
|
|
224
|
+
enabled,
|
|
225
|
+
labels,
|
|
226
|
+
};
|
|
227
|
+
// Filter out undefined properties
|
|
228
|
+
Object.keys(backupPolicy).forEach((key) => {
|
|
229
|
+
if (backupPolicy[key] === undefined) {
|
|
230
|
+
delete backupPolicy[key];
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
// Create an update mask based on the provided fields
|
|
234
|
+
const updateMask = {
|
|
235
|
+
paths: Object.keys(backupPolicy).filter((key) => key !== 'name'),
|
|
236
|
+
};
|
|
237
|
+
// Call the API to update a backup policy
|
|
238
|
+
const [operation] = await client.updateBackupPolicy({
|
|
239
|
+
backupPolicy,
|
|
240
|
+
updateMask,
|
|
241
|
+
});
|
|
242
|
+
const result = {
|
|
243
|
+
name: operation.metadata ? operation.metadata.target || '' : '',
|
|
244
|
+
operationId: operation.name || '',
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
content: [
|
|
248
|
+
{
|
|
249
|
+
type: 'text',
|
|
250
|
+
text: `Updated backup policy: ${result.name}. Operation ID: ${result.operationId}`,
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
structuredContent: result,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
return {
|
|
258
|
+
content: [
|
|
259
|
+
{
|
|
260
|
+
type: 'text',
|
|
261
|
+
text: `Failed to update backup policy: ${error.message}`,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
isError: true,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
// Export tool handler mappings
|
|
269
|
+
export const backupPolicyHandlers = {
|
|
270
|
+
[createBackupPolicyTool.name]: createBackupPolicyHandler,
|
|
271
|
+
[deleteBackupPolicyTool.name]: deleteBackupPolicyHandler,
|
|
272
|
+
[getBackupPolicyTool.name]: getBackupPolicyHandler,
|
|
273
|
+
[listBackupPoliciesTool.name]: listBackupPoliciesHandler,
|
|
274
|
+
[updateBackupPolicyTool.name]: updateBackupPolicyHandler,
|
|
275
|
+
};
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { NetAppClientFactory } from '../../utils/netapp-client-factory.js';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
const log = logger.child({ module: 'backup-vault-handler' });
|
|
4
|
+
// Helper to format backup vault data for responses
|
|
5
|
+
function formatBackupVaultData(backupVault) {
|
|
6
|
+
const result = {};
|
|
7
|
+
if (!backupVault)
|
|
8
|
+
return result;
|
|
9
|
+
if (backupVault.name) {
|
|
10
|
+
// Extract backupVaultId from name (last part after last slash)
|
|
11
|
+
const nameParts = backupVault.name.split('/');
|
|
12
|
+
result.name = backupVault.name;
|
|
13
|
+
result.backupVaultId = nameParts[nameParts.length - 1];
|
|
14
|
+
}
|
|
15
|
+
// Copy basic properties
|
|
16
|
+
if (backupVault.state)
|
|
17
|
+
result.state = backupVault.state;
|
|
18
|
+
// Format timestamps if they exist
|
|
19
|
+
if (backupVault.createTime) {
|
|
20
|
+
result.createTime = new Date(backupVault.createTime.seconds * 1000);
|
|
21
|
+
}
|
|
22
|
+
if (backupVault.updateTime) {
|
|
23
|
+
result.updateTime = new Date(backupVault.updateTime.seconds * 1000);
|
|
24
|
+
}
|
|
25
|
+
// Copy required properties according to the schema
|
|
26
|
+
if (backupVault.backupVaultType)
|
|
27
|
+
result.backupVaultType = backupVault.backupVaultType;
|
|
28
|
+
if (backupVault.sourceRegion)
|
|
29
|
+
result.sourceRegion = backupVault.sourceRegion;
|
|
30
|
+
if (backupVault.backupRegion)
|
|
31
|
+
result.backupRegion = backupVault.backupRegion;
|
|
32
|
+
if (backupVault.sourceBackupVault)
|
|
33
|
+
result.sourceBackupVault = backupVault.sourceBackupVault;
|
|
34
|
+
if (backupVault.destinationBackupVault)
|
|
35
|
+
result.destinationBackupVault = backupVault.destinationBackupVault;
|
|
36
|
+
// Copy backup retention policy if it exists
|
|
37
|
+
if (backupVault.backupRetentionPolicy) {
|
|
38
|
+
result.backupRetentionPolicy = {
|
|
39
|
+
backupMinimumEnforcedRetentionDays: backupVault.backupRetentionPolicy.backupMinimumEnforcedRetentionDays || 0,
|
|
40
|
+
dailyBackupImmutable: backupVault.backupRetentionPolicy.dailyBackupImmutable || false,
|
|
41
|
+
weeklyBackupImmutable: backupVault.backupRetentionPolicy.weeklyBackupImmutable || false,
|
|
42
|
+
monthlyBackupImmutable: backupVault.backupRetentionPolicy.monthlyBackupImmutable || false,
|
|
43
|
+
manualBackupImmutable: backupVault.backupRetentionPolicy.manualBackupImmutable || false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Copy optional properties
|
|
47
|
+
if (backupVault.description)
|
|
48
|
+
result.description = backupVault.description;
|
|
49
|
+
if (backupVault.labels)
|
|
50
|
+
result.labels = backupVault.labels;
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
// Create Backup Vault Handler
|
|
54
|
+
export const createBackupVaultHandler = async (args) => {
|
|
55
|
+
try {
|
|
56
|
+
const { projectId, location, backupVaultId, description, labels } = args;
|
|
57
|
+
// Create a new NetApp client using the factory
|
|
58
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
59
|
+
// Format the parent path for the backup vault
|
|
60
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
61
|
+
// Create the backup vault request
|
|
62
|
+
const request = {
|
|
63
|
+
parent,
|
|
64
|
+
backupVaultId,
|
|
65
|
+
backupVault: {
|
|
66
|
+
description,
|
|
67
|
+
labels,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
// Create the backup vault
|
|
71
|
+
const [operation] = await netAppClient.createBackupVault(request);
|
|
72
|
+
// Extract the operation name for tracking
|
|
73
|
+
const operationName = operation.name;
|
|
74
|
+
// Construct the backup vault name
|
|
75
|
+
const backupVaultName = `${parent}/backupVaults/${backupVaultId}`;
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `Backup vault creation initiated. Operation ID: ${operationName}`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
structuredContent: {
|
|
84
|
+
name: backupVaultName,
|
|
85
|
+
operationId: operationName,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
log.error({ err: error }, 'Error creating backup vault');
|
|
91
|
+
let errorMessage = `Failed to create backup vault: ${error.message}`;
|
|
92
|
+
// Handle specific error types and provide useful error messages
|
|
93
|
+
if (error.code === 6) {
|
|
94
|
+
// ALREADY_EXISTS
|
|
95
|
+
errorMessage = `Backup vault ${args.backupVaultId} already exists in project ${args.projectId}, location ${args.location}`;
|
|
96
|
+
}
|
|
97
|
+
else if (error.code === 7) {
|
|
98
|
+
// PERMISSION_DENIED
|
|
99
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
100
|
+
}
|
|
101
|
+
else if (error.code === 5) {
|
|
102
|
+
// NOT_FOUND
|
|
103
|
+
errorMessage = `Project ${args.projectId} or location ${args.location} not found`;
|
|
104
|
+
}
|
|
105
|
+
else if (error.code === 3) {
|
|
106
|
+
// INVALID_ARGUMENT
|
|
107
|
+
errorMessage = `Invalid argument: ${error.message}`;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
isError: true,
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: errorMessage,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
// Delete Backup Vault Handler
|
|
121
|
+
export const deleteBackupVaultHandler = async (args) => {
|
|
122
|
+
try {
|
|
123
|
+
const { projectId, location, backupVaultId } = args;
|
|
124
|
+
// Create a new NetApp client using the factory
|
|
125
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
126
|
+
// Format the name for the backup vault
|
|
127
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
|
|
128
|
+
// Delete the backup vault
|
|
129
|
+
const request = { name };
|
|
130
|
+
const [operation] = await netAppClient.deleteBackupVault(request);
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: `Backup vault deletion initiated. Operation ID: ${operation.name}`,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
structuredContent: {
|
|
139
|
+
success: true,
|
|
140
|
+
operationId: operation.name,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
log.error({ err: error }, 'Error deleting backup vault');
|
|
146
|
+
let errorMessage = `Failed to delete backup vault: ${error.message}`;
|
|
147
|
+
// Handle specific error types
|
|
148
|
+
if (error.code === 5) {
|
|
149
|
+
// NOT_FOUND
|
|
150
|
+
errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
|
|
151
|
+
}
|
|
152
|
+
else if (error.code === 7) {
|
|
153
|
+
// PERMISSION_DENIED
|
|
154
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
155
|
+
}
|
|
156
|
+
else if (error.code === 9) {
|
|
157
|
+
// FAILED_PRECONDITION
|
|
158
|
+
errorMessage =
|
|
159
|
+
'Failed precondition. The backup vault may have dependent backups that need to be deleted first.';
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
isError: true,
|
|
163
|
+
content: [
|
|
164
|
+
{
|
|
165
|
+
type: 'text',
|
|
166
|
+
text: errorMessage,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
// Get Backup Vault Handler
|
|
173
|
+
export const getBackupVaultHandler = async (args) => {
|
|
174
|
+
try {
|
|
175
|
+
const { projectId, location, backupVaultId } = args;
|
|
176
|
+
// Create a new NetApp client using the factory
|
|
177
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
178
|
+
// Format the name for the backup vault
|
|
179
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
|
|
180
|
+
// Get the backup vault
|
|
181
|
+
const [backupVault] = await netAppClient.getBackupVault({ name });
|
|
182
|
+
// Format the backup vault data
|
|
183
|
+
const formattedData = formatBackupVaultData(backupVault);
|
|
184
|
+
// Default values for required fields if they're not present in the API response
|
|
185
|
+
if (!formattedData.backupVaultType)
|
|
186
|
+
formattedData.backupVaultType = 'STANDARD';
|
|
187
|
+
if (!formattedData.sourceRegion)
|
|
188
|
+
formattedData.sourceRegion = location;
|
|
189
|
+
if (!formattedData.backupRegion)
|
|
190
|
+
formattedData.backupRegion = location;
|
|
191
|
+
if (!formattedData.sourceBackupVault)
|
|
192
|
+
formattedData.sourceBackupVault = '';
|
|
193
|
+
if (!formattedData.destinationBackupVault)
|
|
194
|
+
formattedData.destinationBackupVault = '';
|
|
195
|
+
return {
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: JSON.stringify(formattedData, null, 2),
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
structuredContent: formattedData,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
log.error({ err: error }, 'Error getting backup vault');
|
|
207
|
+
let errorMessage = `Failed to get backup vault: ${error.message}`;
|
|
208
|
+
// Handle specific error types
|
|
209
|
+
if (error.code === 5) {
|
|
210
|
+
// NOT_FOUND
|
|
211
|
+
errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
|
|
212
|
+
}
|
|
213
|
+
else if (error.code === 7) {
|
|
214
|
+
// PERMISSION_DENIED
|
|
215
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
isError: true,
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
221
|
+
type: 'text',
|
|
222
|
+
text: errorMessage,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
// List Backup Vaults Handler
|
|
229
|
+
export const listBackupVaultsHandler = async (args) => {
|
|
230
|
+
try {
|
|
231
|
+
const { projectId, location, filter, pageSize, pageToken } = args;
|
|
232
|
+
// Create a new NetApp client using the factory
|
|
233
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
234
|
+
// Format the parent path for listing backup vaults
|
|
235
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
236
|
+
// List the backup vaults
|
|
237
|
+
const options = {
|
|
238
|
+
parent,
|
|
239
|
+
filter,
|
|
240
|
+
pageSize,
|
|
241
|
+
pageToken,
|
|
242
|
+
};
|
|
243
|
+
const [backupVaultsResponse] = await netAppClient.listBackupVaults(options);
|
|
244
|
+
// Process the response
|
|
245
|
+
let backupVaults = [];
|
|
246
|
+
let nextPageToken;
|
|
247
|
+
// Handle the response structure correctly
|
|
248
|
+
if (backupVaultsResponse) {
|
|
249
|
+
// If it's an array, map each vault
|
|
250
|
+
if (Array.isArray(backupVaultsResponse)) {
|
|
251
|
+
backupVaults = backupVaultsResponse.map((vault) => formatBackupVaultData(vault));
|
|
252
|
+
}
|
|
253
|
+
// Handle as a generic object with any fields
|
|
254
|
+
else {
|
|
255
|
+
const response = backupVaultsResponse;
|
|
256
|
+
// Check for different possible response structures
|
|
257
|
+
if (response.backupVaults && Array.isArray(response.backupVaults)) {
|
|
258
|
+
backupVaults = response.backupVaults.map((vault) => formatBackupVaultData(vault));
|
|
259
|
+
}
|
|
260
|
+
if (response.nextPageToken) {
|
|
261
|
+
nextPageToken = response.nextPageToken;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
content: [
|
|
267
|
+
{
|
|
268
|
+
type: 'text',
|
|
269
|
+
text: `Retrieved ${backupVaults.length} backup vault(s) from project ${projectId}, location ${location}.\n` +
|
|
270
|
+
JSON.stringify(backupVaults, null, 2),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
structuredContent: {
|
|
274
|
+
backupVaults,
|
|
275
|
+
nextPageToken,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
log.error({ err: error }, 'Error listing backup vaults');
|
|
281
|
+
let errorMessage = `Failed to list backup vaults: ${error.message}`;
|
|
282
|
+
// Handle specific error types
|
|
283
|
+
if (error.code === 5) {
|
|
284
|
+
// NOT_FOUND
|
|
285
|
+
errorMessage = `Project or location not found: projects/${args.projectId}/locations/${args.location}`;
|
|
286
|
+
}
|
|
287
|
+
else if (error.code === 7) {
|
|
288
|
+
// PERMISSION_DENIED
|
|
289
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
290
|
+
}
|
|
291
|
+
else if (error.code === 3) {
|
|
292
|
+
// INVALID_ARGUMENT
|
|
293
|
+
errorMessage = `Invalid argument: ${error.message}`;
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
isError: true,
|
|
297
|
+
content: [
|
|
298
|
+
{
|
|
299
|
+
type: 'text',
|
|
300
|
+
text: errorMessage,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
// Update Backup Vault Handler
|
|
307
|
+
export const updateBackupVaultHandler = async (args) => {
|
|
308
|
+
try {
|
|
309
|
+
const { projectId, location, backupVaultId, description, labels } = args;
|
|
310
|
+
// Create a new NetApp client using the factory
|
|
311
|
+
const netAppClient = NetAppClientFactory.createClient();
|
|
312
|
+
// Format the name for the backup vault
|
|
313
|
+
const name = `projects/${projectId}/locations/${location}/backupVaults/${backupVaultId}`;
|
|
314
|
+
// Create update mask based on provided fields
|
|
315
|
+
const updateMask = [];
|
|
316
|
+
if (description !== undefined)
|
|
317
|
+
updateMask.push('description');
|
|
318
|
+
if (labels !== undefined)
|
|
319
|
+
updateMask.push('labels');
|
|
320
|
+
// Update the backup vault
|
|
321
|
+
const [operation] = await netAppClient.updateBackupVault({
|
|
322
|
+
backupVault: {
|
|
323
|
+
name,
|
|
324
|
+
description,
|
|
325
|
+
labels,
|
|
326
|
+
},
|
|
327
|
+
updateMask: {
|
|
328
|
+
paths: updateMask,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
content: [
|
|
333
|
+
{
|
|
334
|
+
type: 'text',
|
|
335
|
+
text: `Backup vault update initiated. Operation ID: ${operation.name}`,
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
structuredContent: {
|
|
339
|
+
name,
|
|
340
|
+
operationId: operation.name,
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
log.error({ err: error }, 'Error updating backup vault');
|
|
346
|
+
let errorMessage = `Failed to update backup vault: ${error.message}`;
|
|
347
|
+
// Handle specific error types
|
|
348
|
+
if (error.code === 5) {
|
|
349
|
+
// NOT_FOUND
|
|
350
|
+
errorMessage = `Backup vault not found: projects/${args.projectId}/locations/${args.location}/backupVaults/${args.backupVaultId}`;
|
|
351
|
+
}
|
|
352
|
+
else if (error.code === 7) {
|
|
353
|
+
// PERMISSION_DENIED
|
|
354
|
+
errorMessage = 'Permission denied. Please check your credentials and access rights.';
|
|
355
|
+
}
|
|
356
|
+
else if (error.code === 3) {
|
|
357
|
+
// INVALID_ARGUMENT
|
|
358
|
+
errorMessage = `Invalid argument: ${error.message}`;
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
isError: true,
|
|
362
|
+
content: [
|
|
363
|
+
{
|
|
364
|
+
type: 'text',
|
|
365
|
+
text: errorMessage,
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
};
|