1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
|
#!/usr/bin/python
# Copyright: (c) 2021, Dell Technologies
# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt)
""" Ansible module for managing SDCs on Dell Technologies (Dell) PowerFlex"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
module: sdc
version_added: '1.0.0'
short_description: Manage SDCs on Dell PowerFlex
description:
- Managing SDCs on PowerFlex storage system includes getting details of SDC
and renaming SDC.
author:
- Akash Shendge (@shenda1) <ansible.team@dell.com>
extends_documentation_fragment:
- dellemc.powerflex.powerflex
options:
sdc_name:
description:
- Name of the SDC.
- Specify either I(sdc_name), I(sdc_id) or I(sdc_ip) for get/rename operation.
- Mutually exclusive with I(sdc_id) and I(sdc_ip).
type: str
sdc_id:
description:
- ID of the SDC.
- Specify either I(sdc_name), I(sdc_id) or I(sdc_ip) for get/rename operation.
- Mutually exclusive with I(sdc_name) and I(sdc_ip).
type: str
sdc_ip:
description:
- IP of the SDC.
- Specify either I(sdc_name), I(sdc_id) or I(sdc_ip) for get/rename operation.
- Mutually exclusive with I(sdc_id) and I(sdc_name).
type: str
sdc_new_name:
description:
- New name of the SDC. Used to rename the SDC.
type: str
performance_profile:
description:
- Define the performance profile as I(Compact) or I(HighPerformance).
- The high performance profile configures a predefined set of parameters for very high performance use cases.
choices: ['Compact', 'HighPerformance']
type: str
state:
description:
- State of the SDC.
choices: ['present', 'absent']
required: true
type: str
notes:
- The I(check_mode) is not supported.
'''
EXAMPLES = r'''
- name: Get SDC details using SDC ip
dellemc.powerflex.sdc:
hostname: "{{hostname}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
sdc_ip: "{{sdc_ip}}"
state: "present"
- name: Rename SDC using SDC name
dellemc.powerflex.sdc:
hostname: "{{hostname}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
sdc_name: "centos_sdc"
sdc_new_name: "centos_sdc_renamed"
state: "present"
- name: Modify performance profile of SDC using SDC name
dellemc.powerflex.sdc:
hostname: "{{hostname}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
sdc_name: "centos_sdc"
performance_profile: "Compact"
state: "present"
- name: Remove SDC using SDC name
dellemc.powerflex.sdc:
hostname: "{{hostname}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
sdc_name: "centos_sdc"
state: "absent"
'''
RETURN = r'''
changed:
description: Whether or not the resource has changed.
returned: always
type: bool
sample: 'false'
sdc_details:
description: Details of the SDC.
returned: When SDC exists
type: dict
contains:
id:
description: The ID of the SDC.
type: str
name:
description: Name of the SDC.
type: str
sdcIp:
description: IP of the SDC.
type: str
osType:
description: OS type of the SDC.
type: str
mapped_volumes:
description: The details of the mapped volumes.
type: list
contains:
id:
description: The ID of the volume.
type: str
name:
description: The name of the volume.
type: str
volumeType:
description: Type of the volume.
type: str
sdcApproved:
description: Indicates whether an SDC has approved access to the
system.
type: bool
sample: {
"id": "07335d3d00000006",
"installedSoftwareVersionInfo": "R3_6.0.0",
"kernelBuildNumber": null,
"kernelVersion": "3.10.0",
"links": [
{
"href": "/api/instances/Sdc::07335d3d00000006",
"rel": "self"
},
{
"href": "/api/instances/Sdc::07335d3d00000006/relationships/
Statistics",
"rel": "/api/Sdc/relationship/Statistics"
},
{
"href": "/api/instances/Sdc::07335d3d00000006/relationships/
Volume",
"rel": "/api/Sdc/relationship/Volume"
},
{
"href": "/api/instances/System::4a54a8ba6df0690f",
"rel": "/api/parent/relationship/systemId"
}
],
"mapped_volumes": [],
"mdmConnectionState": "Disconnected",
"memoryAllocationFailure": null,
"name": "LGLAP203",
"osType": "Linux",
"peerMdmId": null,
"perfProfile": "HighPerformance",
"sdcApproved": true,
"sdcApprovedIps": null,
"sdcGuid": "F8ECB844-23B8-4629-92BB-B6E49A1744CB",
"sdcIp": "N/A",
"sdcIps": null,
"sdcType": "AppSdc",
"sdrId": null,
"socketAllocationFailure": null,
"softwareVersionInfo": "R3_6.0.0",
"systemId": "4a54a8ba6df0690f",
"versionInfo": "R3_6.0.0"
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell\
import utils
LOG = utils.get_logger('sdc')
class PowerFlexSdc(object):
"""Class with SDC operations"""
def __init__(self):
""" Define all parameters required by this module"""
self.module_params = utils.get_powerflex_gateway_host_parameters()
self.module_params.update(get_powerflex_sdc_parameters())
mutually_exclusive = [['sdc_id', 'sdc_ip', 'sdc_name']]
required_one_of = [['sdc_id', 'sdc_ip', 'sdc_name']]
# initialize the Ansible module
self.module = AnsibleModule(
argument_spec=self.module_params,
supports_check_mode=False,
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of)
utils.ensure_required_libs(self.module)
try:
self.powerflex_conn = utils.get_powerflex_gateway_host_connection(
self.module.params)
LOG.info("Got the PowerFlex system connection object instance")
except Exception as e:
LOG.error(str(e))
self.module.fail_json(msg=str(e))
def rename_sdc(self, sdc_id, new_name):
"""Rename SDC
:param sdc_id: The ID of the SDC
:param new_name: The new name of the SDC
:return: Boolean indicating if rename operation is successful
"""
try:
self.powerflex_conn.sdc.rename(sdc_id=sdc_id, name=new_name)
return True
except Exception as e:
errormsg = "Failed to rename SDC %s with error %s" % (sdc_id,
str(e))
LOG.error(errormsg)
self.module.fail_json(msg=errormsg)
def get_mapped_volumes(self, sdc_id):
"""Get volumes mapped to SDC
:param sdc_id: The ID of the SDC
:return: List containing volume details mapped to SDC
"""
try:
resp = self.powerflex_conn.sdc.get_mapped_volumes(sdc_id=sdc_id)
return resp
except Exception as e:
errormsg = "Failed to get the volumes mapped to SDC %s with " \
"error %s" % (sdc_id, str(e))
LOG.error(errormsg)
self.module.fail_json(msg=errormsg)
def get_sdc(self, sdc_name=None, sdc_ip=None, sdc_id=None):
"""Get the SDC Details
:param sdc_name: The name of the SDC
:param sdc_ip: The IP of the SDC
:param sdc_id: The ID of the SDC
:return: The dict containing SDC details
"""
if sdc_name:
id_ip_name = sdc_name
elif sdc_ip:
id_ip_name = sdc_ip
else:
id_ip_name = sdc_id
try:
if sdc_name:
sdc_details = self.powerflex_conn.sdc.get(
filter_fields={'name': sdc_name})
elif sdc_ip:
sdc_details = self.powerflex_conn.sdc.get(
filter_fields={'sdcIp': sdc_ip})
else:
sdc_details = self.powerflex_conn.sdc.get(
filter_fields={'id': sdc_id})
if len(sdc_details) == 0:
error_msg = "Unable to find SDC with identifier %s" \
% id_ip_name
LOG.error(error_msg)
return None
sdc_details[0]['mapped_volumes'] = self.get_mapped_volumes(
sdc_details[0]['id'])
return sdc_details[0]
except Exception as e:
errormsg = "Failed to get the SDC %s with error %s" % (
id_ip_name, str(e))
LOG.error(errormsg)
self.module.fail_json(msg=errormsg)
def validate_parameters(self, sdc_name=None, sdc_id=None, sdc_ip=None):
"""Validate the input parameters"""
sdc_identifiers = ['sdc_name', 'sdc_id', 'sdc_ip']
for param in sdc_identifiers:
if self.module.params[param] is not None and \
len(self.module.params[param].strip()) == 0:
msg = f"Please provide valid {param}"
LOG.error(msg)
self.module.fail_json(msg=msg)
def remove(self, sdc_id):
"""Remove the SDC"""
try:
LOG.info(msg=f"Removing SDC {sdc_id}")
self.powerflex_conn.sdc.delete(sdc_id)
return True
except Exception as e:
errormsg = f"Removing SDC {sdc_id} failed with error {str(e)}"
LOG.error(errormsg)
self.module.fail_json(msg=errormsg)
def set_performance_profile(self, sdc_id, performance_profile):
"""Set performance profile of SDC"""
try:
LOG.info(msg=f"Setting performance profile of SDC {sdc_id}")
self.powerflex_conn.sdc.set_performance_profile(sdc_id, performance_profile)
return True
except Exception as e:
errormsg = f"Modifying performance profile of SDC {sdc_id} failed with error {str(e)}"
LOG.error(errormsg)
self.module.fail_json(msg=errormsg)
def validate_input(self, sdc_details, sdc_new_name, state, id_ip_name):
if state == 'present' and not sdc_details:
error_msg = 'Could not find any SDC instance with ' \
'identifier %s.' % id_ip_name
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
if sdc_new_name and len(sdc_new_name.strip()) == 0:
self.module.fail_json(msg="Provide valid SDC name to rename to.")
def perform_modify(self, sdc_details, sdc_new_name, performance_profile):
changed = False
if sdc_new_name is not None and sdc_new_name != sdc_details['name']:
changed = self.rename_sdc(sdc_details['id'], sdc_new_name)
if performance_profile and performance_profile != sdc_details['perfProfile']:
changed = self.set_performance_profile(sdc_details['id'], performance_profile)
return changed
def perform_module_operation(self):
"""
Perform different actions on SDC based on parameters passed in
the playbook
"""
sdc_name = self.module.params['sdc_name']
sdc_id = self.module.params['sdc_id']
sdc_ip = self.module.params['sdc_ip']
sdc_new_name = self.module.params['sdc_new_name']
performance_profile = self.module.params['performance_profile']
state = self.module.params['state']
# result is a dictionary to contain end state and SDC details
changed = False
result = dict(
changed=False,
sdc_details={}
)
self.validate_parameters(sdc_name, sdc_id, sdc_ip)
sdc_details = self.get_sdc(sdc_name=sdc_name, sdc_id=sdc_id,
sdc_ip=sdc_ip)
id_ip_name = sdc_name or sdc_ip or sdc_id
self.validate_input(sdc_details, sdc_new_name, state, id_ip_name)
if state == 'absent' and sdc_details:
changed = self.remove(sdc_details['id'])
if state == 'present' and sdc_details:
changed = self.perform_modify(sdc_details, sdc_new_name, performance_profile)
if changed:
sdc_details = self.get_sdc(sdc_name=sdc_new_name or sdc_name,
sdc_id=sdc_id, sdc_ip=sdc_ip)
result['sdc_details'] = sdc_details
result['changed'] = changed
self.module.exit_json(**result)
def get_powerflex_sdc_parameters():
"""This method provide parameter required for the Ansible SDC module on
PowerFlex"""
return dict(
sdc_id=dict(),
sdc_ip=dict(),
sdc_name=dict(),
sdc_new_name=dict(), performance_profile=dict(choices=['Compact', 'HighPerformance']),
state=dict(required=True, type='str', choices=['present', 'absent'])
)
def main():
""" Create PowerFlex SDC object and perform actions on it
based on user input from playbook"""
obj = PowerFlexSdc()
obj.perform_module_operation()
if __name__ == '__main__':
main()
|