-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathprovisioning.py
2378 lines (2105 loc) · 102 KB
/
provisioning.py
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
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dell Technologies
"""Collection of provisioning related functions for PowerStore"""
from PyPowerStore.client import Client
from PyPowerStore.utils import constants, helpers
# TODO: kept LOG as global for now will improve it to avoid overriding
LOG = helpers.get_logger(__name__)
class Provisioning:
"""Provisioning related functionality for PowerStore."""
def __init__(self, server_ip, username, password, verify,
application_type, timeout, enable_log=False, port_no=None):
""" Initializes Provisioning Class
:param server_ip: The array IP
:type server_ip: str
:param port_no: The port number
:type port_no: int
:param username: array username
:type username: str
:param password: array password
:type password: str
:param verify: Whether the SSL cert will be verified
:type verify: bool
:param application_type: Application Type
:type application_type: str
:param timeout: How long to wait for the server to send data before
giving up
:type timeout: float
:param enable_log: (optional) Whether to enable log or not
:type enable_log: bool
"""
global LOG
if port_no is None:
port_no = 443
self.server_ip = server_ip + ":" + str(port_no)
self.client = Client(username, password, verify, application_type,
timeout, enable_log=enable_log)
LOG = helpers.get_logger(__name__, enable_log=enable_log)
helpers.set_provisioning_obj(self)
def get_array_version(self):
"""Get the software version of this array.
:return: software version details
:rtype: str
"""
LOG.info("Getting the software version of this array")
querystring = helpers.prepare_querystring(
constants.SELECT_VERSION)
LOG.info("Querystring: '%s'" % querystring)
sw_versions = self.client.request(constants.GET,
constants.GET_SOFTWARE_VERSION.format
(self.server_ip), payload=None,
querystring=querystring)
version = None
if sw_versions and len(sw_versions) >0:
version = sw_versions[0]['release_version']
return version
def create_volume(self, name, size, description=None,
volume_group_id=None, protection_policy_id=None,
performance_policy_id=None, app_type=None,
app_type_other=None, appliance_id=None):
"""Create a volume.
:param name: The name of the volume
:param size: The size of the volume
:param description: (optional) The description given to the volume
:param volume_group_id: (optional) The volume group ID
:param protection_policy_id: (optional) The protection policy ID
:param performance_policy_id: (optional) The performance policy ID
:param app_type: (optional) The application type
:param app_type_other: (optional) Describes application type when
app_type is set to other
:param appliance_id: (optional) The appliance ID
"""
if app_type is not None and not helpers.is_malka_or_higher():
raise Exception("'app_type' parameter is supported only from "
"Powerstore version 2.1.0.0 onwards")
LOG.info("Creating volume: '%s'" % name)
payload = self._prepare_create_volume_payload(name, size,
description,
volume_group_id,
protection_policy_id,
performance_policy_id,
app_type,
app_type_other,
appliance_id)
self.client.request(constants.POST,
constants.VOLUME_CREATE_URL.format(
self.server_ip), payload)
def _prepare_create_volume_payload(self, name, size,
description,
volume_group_id,
protection_policy_id,
performance_policy_id,
app_type,
app_type_other,
appliance_id):
create_volume_dict = dict()
if name is not None:
create_volume_dict['name'] = name
if size is not None:
create_volume_dict['size'] = size
if description is not None:
create_volume_dict['description'] = description
if volume_group_id is not None:
create_volume_dict['volume_group_id'] = volume_group_id
if protection_policy_id is not None:
create_volume_dict['protection_policy_id'] = protection_policy_id
if performance_policy_id is not None:
create_volume_dict['performance_policy_id'] = \
performance_policy_id
if app_type is not None:
create_volume_dict['app_type'] = app_type
if app_type_other is not None:
create_volume_dict['app_type_other'] = app_type_other
if appliance_id is not None:
create_volume_dict['appliance_id'] = appliance_id
return create_volume_dict
def delete_volume(self, volume_id):
"""Delete a volume.
:param volume_id: The Volume ID
:type volume_id: str
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Deleting volume: '%s'" % volume_id)
return self.client.request(
constants.DELETE, constants.DELETE_VOLUME_URL.format(
self.server_ip, volume_id),
payload=None)
def modify_volume(self, volume_id, name=None, description=None, size=None,
protection_policy_id=None, performance_policy_id=None,
app_type=None, app_type_other=None):
"""Modify a volume.
:param volume_id: The volume ID
:type volume_id: str
:param name: The name of the volume
:type name: str
:param description: The description of the volume
:type description: str
:param size: The size of the volume
:type size: str
:param protection_policy_id: The protection policy ID
:type protection_policy_id: str
:param performance_policy_id: The performance policy ID
:type performance_policy_id: str
:param app_type: The application type
:type app_type: str
:param app_type_other: Describes application type when
app_type is set to other
:return: None if success else raise exception
:rtype: None
"""
if app_type is not None and not helpers.is_malka_or_higher():
raise Exception("'app_type' parameter is supported only from "
"Powerstore version 2.1.0.0 onwards")
LOG.info("Modifying volume: '%s'" % volume_id)
payload = self.\
_prepare_modify_volume_payload(name,
description,
size,
protection_policy_id,
performance_policy_id,
app_type,
app_type_other)
return self.client.request(
constants.PATCH, constants.MODIFY_VOLUME_URL.format(
self.server_ip, volume_id),
payload)
def _prepare_modify_volume_payload(self, name=None, description=None,
size=None,
protection_policy_id=None,
performance_policy_id=None,
app_type=None,
app_type_other=None):
modify_volume_dict = dict()
if name is not None:
modify_volume_dict['name'] = name
if description is not None:
modify_volume_dict['description'] = description
if size is not None:
modify_volume_dict['size'] = size
if protection_policy_id is not None:
modify_volume_dict['protection_policy_id'] = protection_policy_id
if performance_policy_id is not None:
modify_volume_dict['performance_policy_id'] = \
performance_policy_id
if app_type is not None:
modify_volume_dict['app_type'] = app_type
if app_type_other is not None:
modify_volume_dict['app_type_other'] = app_type_other
return modify_volume_dict
def clone_volume(self, volume_id, name=None,
description=None, host_id=None,
host_group_id=None,
logical_unit_number=None,
protection_policy_id=None,
performance_policy_id=None):
"""Clone a volume.
:param volume_id: The volume ID
:type volume_id: str
:param name: Name of the clone
:type name: str
:param description: Description of the clone
:type description: str
:param host_id: Unique identifier of the host to be attached to the clone.
:type host_id: str
:param host_group_id: Unique identifier of the host group to be attached to the clone.
:type host_group_id: str
:param logical_unit_number: Optional logical unit number when creating a mapped volume.
:type logical_unit_number: int
:param protection_policy_id: The protection policy ID
:type protection_policy_id: str
:param performance_policy_id: The performance policy ID
:type performance_policy_id: str
:return: 'id' Unique identifier of the new clone volume if success else raise exception
:rtype: dict
"""
LOG.info("Cloning the volume: '%s'" % volume_id)
payload = self.\
_prepare_clone_volume_payload(name,
description,
host_id,
host_group_id,
logical_unit_number,
protection_policy_id,
performance_policy_id)
return self.client.request(
constants.POST, constants.CLONE_VOLUME_URL.format(
self.server_ip, volume_id),
payload)
def _prepare_clone_volume_payload(self, name=None, description=None, host_id=None,
host_group_id=None,
logical_unit_number=None,
protection_policy_id=None,
performance_policy_id=None):
clone_volume_dict = dict()
if name is not None:
clone_volume_dict['name'] = name
if description is not None:
clone_volume_dict['description'] = description
if host_id is not None:
clone_volume_dict['host_id'] = host_id
if host_group_id is not None:
clone_volume_dict['host_group_id'] = host_group_id
if logical_unit_number is not None:
clone_volume_dict['logical_unit_number'] = logical_unit_number
if protection_policy_id is not None:
clone_volume_dict['protection_policy_id'] = protection_policy_id
if performance_policy_id is not None:
clone_volume_dict['performance_policy_id'] = \
performance_policy_id
return clone_volume_dict
def refresh_volume(self, volume_id, volume_id_to_refresh_from=None,
create_backup_snap=None,
backup_snap_name=None,
backup_snap_description=None,
backup_snap_expiration_timestamp=None,
backup_snap_performance_policy_id=None):
"""Refresh a volume.
:param volume_id: The volume ID
:type volume_id: str
:param volume_id_to_refresh_from: Unique identifier of the source volume that will be used for the refresh operation
:type volume_id_to_refresh_from: str
:param create_backup_snap: Indicates whether a backup snapshot of the target volume will be created before it is refreshed from the source volume
:type create_backup_snap: bool
:param backup_snap_name: Name of the backup snapshot to be created. The default name of the volume snapshot is the date and time when the snapshot is taken
:type backup_snap_name: str
:param backup_snap_description: Description of the backup snapshot.
:type backup_snap_description: str
:param backup_snap_performance_policy_id: Unique identifier of the performance policy assigned to the snapshot.
:type backup_snap_performance_policy_id: str
:param backup_snap_expiration_timestamp: Time at which the backup snapshot will expire.
:type backup_snap_expiration_timestamp: str
:return: 'backup_snapshot_id' Unique identifier of the backup snapshot of the target volume, if one is created prior to the refresh operation.
:rtype: dict
"""
LOG.info("Refreshing the volume: '%s'" % volume_id)
payload = self.\
_prepare_refresh_volume_payload(volume_id_to_refresh_from,
create_backup_snap,
backup_snap_name,
backup_snap_description,
backup_snap_expiration_timestamp,
backup_snap_performance_policy_id)
return self.client.request(
constants.POST, constants.REFRESH_VOLUME_URL.format(
self.server_ip, volume_id),
payload)
def _prepare_refresh_volume_payload(self, volume_id_to_refresh_from=None,
create_backup_snap=None,
backup_snap_name=None,
backup_snap_description=None,
backup_snap_expiration_timestamp=None,
backup_snap_performance_policy_id=None):
refresh_volume_dict = dict()
if volume_id_to_refresh_from is not None:
refresh_volume_dict['from_object_id'] = volume_id_to_refresh_from
if create_backup_snap is not None:
refresh_volume_dict['create_backup_snap'] = create_backup_snap
refresh_volume_dict['backup_snap_profile'] = {}
if backup_snap_name is not None:
refresh_volume_dict['backup_snap_profile']['name'] = backup_snap_name
if backup_snap_description is not None:
refresh_volume_dict['backup_snap_profile']['description'] = backup_snap_description
if backup_snap_expiration_timestamp is not None:
refresh_volume_dict['backup_snap_profile']['expiration_timestamp'] = \
backup_snap_expiration_timestamp
if backup_snap_performance_policy_id is not None:
refresh_volume_dict['backup_snap_profile']['performance_policy_id'] = \
backup_snap_performance_policy_id
return refresh_volume_dict
def restore_volume(self, volume_id, snap_id_to_restore_from=None,
create_backup_snap=None,
backup_snap_name=None,
backup_snap_description=None,
backup_snap_expiration_timestamp=None,
backup_snap_performance_policy_id=None):
"""Restore a volume.
:param volume_id: The volume ID
:type volume_id: str
:param snap_id_to_restore_from: Unique identifier of the source snapshot that will be used for the restore operation.
:type snap_id_to_restore_from: str
:param create_backup_snap: Indicates whether a backup snapshot of the target volume will be created before it is restored from the snapshot.
:type create_backup_snap: bool
:param backup_snap_name: Name of the backup snapshot to be created. The default name of the volume snapshot is the date and time when the snapshot is taken
:type backup_snap_name: str
:param backup_snap_description: Description of the backup snapshot.
:type backup_snap_description: str
:param backup_snap_performance_policy_id: Unique identifier of the performance policy assigned to the snapshot.
:type backup_snap_performance_policy_id: str
:param backup_snap_expiration_timestamp: Time at which the backup snapshot will expire.
:type backup_snap_expiration_timestamp: str
:return: 'backup_snapshot_id' Unique identifier of the backup snapshot of the target volume, if one is created prior to the restore operation.
:rtype: dict
"""
LOG.info("Restoring the volume: '%s'" % volume_id)
payload = self.\
_prepare_restore_volume_payload(snap_id_to_restore_from,
create_backup_snap,
backup_snap_name,
backup_snap_description,
backup_snap_expiration_timestamp,
backup_snap_performance_policy_id)
return self.client.request(
constants.POST, constants.RESTORE_VOLUME_URL.format(
self.server_ip, volume_id),
payload)
def _prepare_restore_volume_payload(self, snap_id_to_restore_from=None,
create_backup_snap=None,
backup_snap_name=None,
backup_snap_description=None,
backup_snap_expiration_timestamp=None,
backup_snap_performance_policy_id=None):
refresh_volume_dict = dict()
if snap_id_to_restore_from is not None:
refresh_volume_dict['from_snap_id'] = snap_id_to_restore_from
if create_backup_snap is not None:
refresh_volume_dict['create_backup_snap'] = create_backup_snap
refresh_volume_dict['backup_snap_profile'] = {}
if backup_snap_name is not None:
refresh_volume_dict['backup_snap_profile']['name'] = backup_snap_name
if backup_snap_description is not None:
refresh_volume_dict['backup_snap_profile']['description'] = backup_snap_description
if backup_snap_expiration_timestamp is not None:
refresh_volume_dict['backup_snap_profile']['expiration_timestamp'] = \
backup_snap_expiration_timestamp
if backup_snap_performance_policy_id is not None:
refresh_volume_dict['backup_snap_profile']['performance_policy_id'] = \
backup_snap_performance_policy_id
return refresh_volume_dict
def clone_filesystem(self, filesystem_id, advance_parameters):
"""Clone a filesystem.
:param filesystem_id: The filesystem ID
:type filesystem_id: str
:param advance_parameters: Advance attributes
:type advance_parameters: str
:return: Unique identifier of the new instance created if success else raise exception
:rtype: dict
"""
LOG.info("Cloning the filesystem: '%s'" % filesystem_id)
payload = dict()
if advance_parameters:
for key, value in advance_parameters.items():
if key in constants.FILESYSTEM_PRIME and \
not helpers.is_foot_hill_prime_or_higher():
raise Exception( key + " is supported for PowerStore" \
" version 3.0.0.0 and above.")
payload[key] = value
return self.client.request(
constants.POST, constants.CLONE_FILESYSTEM_URL.format(
self.server_ip, filesystem_id),
payload)
def restore_filesystem(self, snapshot_id, backup_snap_name=None):
"""Restore a filesystem.
:param snapshot_id: Unique identifier of the file system snapshot.
:type snapshot_id: str
:param backup_snap_name: Name of the backup snap to be created before the restore operation occurs. If no name is specified, no backup copy will be made.
:type backup_snap_name: str
:return: Unique identifier of the backup snapshot set or None if backup_snap_name is None
if success else raise exception
:rtype: dict
"""
LOG.info("Restoring the filesystem from snapshot: '%s'" % snapshot_id)
payload = {}
if backup_snap_name is not None:
payload['copy_name'] = backup_snap_name
return self.client.request(
constants.POST, constants.RESTORE_FILESYSTEM_URL.format(
self.server_ip, snapshot_id),
payload)
def refresh_filesystem(self, snapshot_id):
"""Refresh a filesystem.
:param snapshot_id: Unique identifier of the file system snapshot.
:type snapshot_id: str
:return: None if success else raise exception.
:rtype: None
"""
LOG.info("Refreshing the filesystem from snapshot: '%s'" % snapshot_id)
return self.client.request(
constants.POST, constants.REFRESH_FILESYSTEM_URL.format(
self.server_ip, snapshot_id))
def add_protection_policy_for_volume(self, volume_id,
protection_policy_id):
"""Add protection policy for volume.
:param volume_id: The volume ID
:type volume_id: str
:param protection_policy_id: The protection policy ID
:type protection_policy_id: str
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Adding protection policy: '%s' for volume: '%s'"
% (protection_policy_id, volume_id))
payload = self.\
_prepare_modify_volume_payload(
protection_policy_id=protection_policy_id)
return self.client.request(constants.PATCH,
constants.MODIFY_VOLUME_URL.format(
self.server_ip, volume_id),
payload)
def remove_protection_policy_for_volume(self, volume_id):
"""Remove protection policy for volume.
:param volume_id: The volume ID
:type volume_id: str
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Removing protection policy for volume: '%s'" % volume_id)
payload = {
'protection_policy_id': ''
}
return self.client.request(constants.PATCH,
constants.MODIFY_VOLUME_URL.format(
self.server_ip, volume_id),
payload)
def map_volume_to_host(self, volume_id, host_id=None,
logical_unit_number=None):
"""Map a volume to a Host.
:param volume_id: The volume ID
:type volume_id: str
:param host_id: (optional) The host ID
:type host_id: str
:param logical_unit_number: (optional) The logical unit number
:type logical_unit_number: str
"""
LOG.info("Mapping volume: '%s' to host" % volume_id)
payload = self._prepare_map_vol_to_host_payload(host_id,
logical_unit_number)
self.client.request(constants.POST,
constants.MAP_VOLUME_TO_HOST_URL.format(
self.server_ip, volume_id), payload)
def _prepare_map_vol_to_host_payload(self, host_id, logical_unit_number):
map_volume_to_host_dict = dict()
if host_id is not None:
map_volume_to_host_dict['host_id'] = host_id
if logical_unit_number is not None:
map_volume_to_host_dict['logical_unit_number'] = \
logical_unit_number
return map_volume_to_host_dict
def map_volume_to_host_group(self, volume_id, host_group_id=None,
logical_unit_number=None):
"""Map a volume to a Host.
:param volume_id: The volume ID
:type volume_id: str
:param host_group_id: (optional) The host group ID
:type host_group_id: str
:param logical_unit_number: (optional) The logical unit number
:type logical_unit_number: str
"""
LOG.info("Mapping volume: '%s' to host group" % volume_id)
payload = self._prepare_map_vol_to_host_grp_payload(
host_group_id, logical_unit_number)
self.client.request(constants.POST,
constants.MAP_VOLUME_TO_HOST_URL.format(
self.server_ip, volume_id), payload)
def _prepare_map_vol_to_host_grp_payload(self, host_grp_id,
logical_unit_number):
map_volume_to_host_dict = dict()
if host_grp_id is not None:
map_volume_to_host_dict['host_group_id'] = host_grp_id
if logical_unit_number is not None:
map_volume_to_host_dict['logical_unit_number'] = \
logical_unit_number
return map_volume_to_host_dict
def unmap_volume_from_host(self, volume_id, host_id):
"""Unmap a Volume from a Host.
:param volume_id: The volume ID
:type volume_id: str
:param host_id: The host ID
:type host_id: str
"""
LOG.info("Unmapping volume: '%s' from host: '%s'"
% (volume_id, host_id))
payload = self._prepare_unmap_vol_from_host_payload(host_id)
self.client.request(constants.POST,
constants.UNMAP_VOLUME_FROM_HOST_URL.format(
self.server_ip, volume_id), payload)
def _prepare_unmap_vol_from_host_payload(self, host_id):
unmap_vol_from_host_dict = dict()
if host_id is not None:
unmap_vol_from_host_dict['host_id'] = host_id
return unmap_vol_from_host_dict
def unmap_volume_from_host_group(self, volume_id, host_group_id):
"""Unmap a Volume from a Host.
:param volume_id: The volume ID
:type volume_id: str
:param host_group_id: The host group ID
:type host_group_id: str
"""
LOG.info("Unmapping volume: '%s' from host group: '%s'"
% (volume_id, host_group_id))
payload = self.\
_prepare_unmap_vol_from_host_grp_payload(host_group_id)
self.client.request(constants.POST,
constants.UNMAP_VOLUME_FROM_HOST_URL.format(
self.server_ip, volume_id), payload)
def _prepare_unmap_vol_from_host_grp_payload(self, host_group_id):
unmap_vol_from_host_dict = dict()
if host_group_id is not None:
unmap_vol_from_host_dict['host_group_id'] = host_group_id
return unmap_vol_from_host_dict
def get_volumes(self, filter_dict=None, all_pages=False):
"""Get a list of volumes.
:param filter_dict: (optional) Filter detail
:type filter_dict: dict
:param all_pages: (optional) Indicates whether to return all element
or not
:type all_pages: bool
:return: Volume details
:rtype: list of dict
"""
LOG.info("Getting volumes with filter: '%s' and all_pages: %s"
% (filter_dict, all_pages))
querystring = helpers.prepare_querystring(
constants.SELECT_ID_AND_NAME,
filter_dict)
LOG.info("Querystring: '%s'" % querystring)
return self.client.request(constants.GET,
constants.GET_VOLUME_LIST_URL.format
(self.server_ip), payload=None,
querystring=querystring,
all_pages=all_pages)
def get_volume_details(self, volume_id):
"""Get details of a volume.
:param volume_id: The volume ID
:type volume_id: str
:return: Volume details
:rtype: dict
"""
LOG.info("Getting volume details by ID: '%s'" % volume_id)
querystring = constants.SELECT_ALL_VOLUME
if helpers.is_foot_hill_prime_or_higher():
querystring = constants.FHP_VOLUME_DETAILS_QUERY
elif helpers.is_foot_hill_or_higher():
querystring = constants.FHC_VOLUME_DETAILS_QUERY
resp = self.client.request(constants.GET,
constants.GET_VOLUME_DETAILS_URL.format
(self.server_ip, volume_id), payload=None,
querystring=querystring)
hlu_details = self.get_host_volume_mapping(volume_id=volume_id)
resp['hlu_details'] = hlu_details
return resp
def get_volume_by_name(self, volume_name):
"""Get details of a volume by name.
:param volume_name: The volume name
:type volume_name: str
:return: Volume details
:rtype: dict
"""
LOG.info("Getting volume details by name: '%s'" % volume_name)
querystring = constants.SELECT_ALL_VOLUME
if helpers.is_foot_hill_prime_or_higher():
querystring = constants.FHP_VOLUME_DETAILS_QUERY
elif helpers.is_foot_hill_or_higher():
querystring = constants.FHC_VOLUME_DETAILS_QUERY
resp = self.client.request(
constants.GET,
constants.GET_VOLUME_BY_NAME_URL.format(self.server_ip),
payload=None, querystring=helpers.prepare_querystring(
querystring, name=constants.EQUALS + volume_name
)
)
if resp:
LOG.info("Getting host volume mapping from vol ID: '%s'"
% resp[0]['id'])
hlu_details = self.get_host_volume_mapping(volume_id=resp[0]['id'])
resp[0]['hlu_details'] = hlu_details
return resp
def configure_metro_volume(self, volume_id, remote_system_id,
remote_appliance_id=None):
""" Configure the metro volume
:param volume_id: ID of the volume
:type volume_id: str
:param remote_system_id: ID of the remote system
:type remote_system_id: str
:param remote_appliance_id: ID of remote appliance to which volume will
be assigned
:type remote_appliance_id: str
:return: ID of metro session
:rtype: str
"""
LOG.info("Configuring the metro volume %s to remote system %s"
% (volume_id, remote_system_id))
if helpers.is_foot_hill_prime_or_higher():
metro_url = constants.CONFIGURE_METRO_VOLUME
payload = dict()
payload['remote_system_id'] = remote_system_id
if remote_appliance_id is not None:
payload['remote_appliance_id'] = remote_appliance_id
return self.client.request(
constants.POST, metro_url.format(self.server_ip, volume_id),
payload=payload)
raise Exception("Not supported for PowerStore version less than 3.0.0.0")
def end_volume_metro_config(self, volume_id, delete_remote_volume=None):
"""
End a metro configuration from a volume and keep both copies.The local
copy will retain its SCSI Identity while the remote volume will get a
new SCSI Identity
:param volume_id: ID of the volume
:type volume_id: str
:param delete_remote_volume: Whether to delete the remote volume during
the removal
:type delete_remote_volume: bool
:return: None if success else raise exception
:rtype: None
"""
LOG.info("End a metro configuration from a volume %s", volume_id)
if helpers.is_foot_hill_prime_or_higher():
end_metro_url = constants.END_METRO_VOLUME
payload = dict()
if delete_remote_volume is not None:
payload['delete_remote_volume'] = delete_remote_volume
return self.client.request(
constants.POST, end_metro_url.format(self.server_ip, volume_id),
payload=payload)
raise Exception("Not supported for PowerStore version less than 3.0.0.0")
def get_hosts(self, filter_dict=None, all_pages=False):
"""Get a list of all the registered hosts.
:param filter_dict: (optional) Filter detail
:type filter_dict: dict
:param all_pages: (optional) Indicates whether to return all element
or not
:type all_pages: bool
:return: Hosts
:rtype: list of dict
"""
LOG.info("Getting hosts with filter: '%s' and all_pages: %s"
% (filter_dict, all_pages))
querystring = helpers.prepare_querystring(constants.SELECT_ID_AND_NAME,
filter_dict)
LOG.info("Querystring: '%s'" % querystring)
return self.client.request(constants.GET,
constants.GET_HOST_LIST_URL.format
(self.server_ip), payload=None,
querystring=querystring,
all_pages=all_pages)
def create_host(self, name, os_type, initiators,
description=None, host_connectivity=None):
"""Register a host on the array.
:param name: The name of the host
:type name: str
:param os_type: The OS type of the host
:type os_type: str
:param initiators: Host initiators
:type initiators: list of dict
:param description: (optional) The description for the host
:type description: str
:param host_connectivity: (optional) Connectivity type for hosts
:type host_connectivity: str
:return: Host ID if success else raise exception
:rtype: dict
"""
LOG.info("Creating host with name: '%s' os_type: '%s' initiators: '%s'"
% (name, os_type, initiators))
payload = self._prepare_create_host_payload(name, description,
os_type, initiators,
host_connectivity)
return self.client.request(constants.POST,
constants.CREATE_HOST_URL.format(
self.server_ip), payload)
def _prepare_create_host_payload(self, name, description,
os_type, initiators, host_connectivity):
create_host_dict = dict()
if name is not None:
create_host_dict['name'] = name
if description is not None:
create_host_dict['description'] = description
if os_type is not None:
create_host_dict['os_type'] = os_type
if initiators is not None:
create_host_dict['initiators'] = initiators
if host_connectivity is not None:
create_host_dict['host_connectivity'] = host_connectivity
return create_host_dict
def get_host_details(self, host_id):
"""Get details of a particular host.
:param host_id: The host ID
:type host_id: str
:return: Host details
:rtype: dict
"""
LOG.info("Getting host details by ID: '%s'" % host_id)
querystring = constants.SELECT_ALL_HOST
if helpers.is_foot_hill_prime_or_higher():
querystring = constants.FHP_HOST_DETAILS_QUERY
elif helpers.is_foot_hill_or_higher():
querystring = constants.FHC_HOST_DETAILS_QUERY
return self.client.request(constants.GET,
constants.GET_HOST_DETAILS_URL.format(
self.server_ip, host_id), payload=None,
querystring=querystring)
def modify_host(self, host_id, name=None, description=None,
remove_initiators=None, add_initiators=None,
modify_initiators=None, host_connectivity=None):
"""Modify a given host - Only one of add, remove
or update in the same request.
:param host_id: The host ID
:type host_id: str
:param name: (optional) The host name
:type name: str
:param description: (optional) The description for the host
:type description: str
:param remove_initiators: (optional) Initiators to be removed
:type remove_initiators: list
:param add_initiators: (optional) Initiators to be added
:type add_initiators: list
:param modify_initiators: (optional) Initiators to be modified
:type modify_initiators: list
:param host_connectivity: (optional)Connectivity types for hosts
:type host_connectivity: str
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Modifying host: '%s'" % host_id)
payload = self._prepare_modify_host_payload(name,
description,
remove_initiators,
add_initiators,
modify_initiators,
host_connectivity)
return self.client.request(
constants.PATCH, constants.MODIFY_HOST_URL.format(
self.server_ip, host_id),
payload)
def _prepare_modify_host_payload(self, name=None, description=None,
remove_initiators=None,
add_initiators=None,
modify_initiators=None,
host_connectivity=None
):
modify_host_dict = dict()
if name is not None:
modify_host_dict['name'] = name
if description is not None:
modify_host_dict['description'] = description
if remove_initiators is not None:
modify_host_dict['remove_initiators'] = remove_initiators
elif add_initiators is not None:
modify_host_dict['add_initiators'] = add_initiators
elif modify_initiators is not None:
modify_host_dict['modify_initiators'] = modify_initiators
elif host_connectivity is not None:
modify_host_dict['host_connectivity'] = host_connectivity
return modify_host_dict
def add_initiators_to_host(self, host_id, add_initiators=None
):
"""Add initiators to host.
:param host_id: The host ID.
:type host_id: str
:param add_initiators: (optional) Initiators to be added.
:type add_initiators: list
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Adding initiators to host: '%s'" % host_id)
payload = self._prepare_modify_host_payload(
add_initiators=add_initiators)
return self.client.request(constants.PATCH,
constants.MODIFY_HOST_URL.format(
self.server_ip, host_id), payload)
def remove_initiators_from_host(self, host_id, remove_initiators=None):
"""Remove initiators from Host.
:param host_id: The host ID
:type host_id: str
:param remove_initiators: (optional) Initiators to be removed
:type remove_initiators: list
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Removing initiators to host: '%s'" % host_id)
payload = self._prepare_modify_host_payload(
remove_initiators=remove_initiators)
return self.client.request(constants.PATCH,
constants.MODIFY_HOST_URL.format(
self.server_ip, host_id), payload)
def delete_host(self, host_id, force=None):
"""Delete a host.
:param host_id: The host ID.
:type host_id: str
:param force: (optional) The force_internal flag.
:type force: bool
:return: None if success else raise exception
:rtype: None
"""
LOG.info("Deleting host: '%s'" % host_id)
if force:
payload = {"force_internal": force}
else:
payload = None
return self.client.request(
constants.DELETE, constants.DELETE_HOST_URL.format(
self.server_ip, host_id),
payload)
def get_host_by_name(self, host_name):
"""Get details of a Host with its name.
:param host_name: The Host name.
:type host_name: str
:return: Host details
:rtype: dict
"""
LOG.info("Getting host details by name: '%s'" % host_name)
querystring = constants.SELECT_ALL_HOST
if helpers.is_foot_hill_prime_or_higher():
querystring = constants.FHP_HOST_DETAILS_QUERY
elif helpers.is_foot_hill_or_higher():
querystring = constants.FHC_HOST_DETAILS_QUERY
return self.client.request(
constants.GET,
constants.GET_HOST_BY_NAME_URL.format(self.server_ip),
payload=None, querystring=helpers.prepare_querystring(
querystring, name=constants.EQUALS + host_name
)
)
def get_host_group_list(self, filter_dict=None, all_pages=False):
"""Get a list of all host groups.
:param filter_dict: (optional) Filter detail
:type filter_dict: dict
:param all_pages: (optional) Indicates whether to return all element
or not
:type all_pages: bool
:return: Hosts
:rtype: list of dict
"""
LOG.info("Getting hostgroup with filter: '%s' and all_pages: %s"
% (filter_dict, all_pages))
querystring = helpers.prepare_querystring(
constants.SELECT_ID_AND_NAME,
filter_dict)
LOG.info("Querystring: '%s'" % querystring)
return self.client.request(constants.GET,
constants.GET_HOST_GROUP_LIST_URL.format(
self.server_ip), payload=None,
querystring=querystring,
all_pages=all_pages)
def create_host_group(self, name, host_ids, description=None):
"""Create a Host Group.
:param name: The name of the host group
:type name: str
:param host_ids: Host IDs
:type host_ids: list
:param description: (optional) The description for the host group
:type description: str
:return: Host group ID if success else raise exception
:rtype: dict
"""
LOG.info("Creating hostgroup: '%s' with host_ids: '%s'"
% (name, host_ids))
payload = self._prepare_create_host_group_payload(