From 94784c6e634e1435ce85171a4650750e243a22cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 21:18:35 +0000 Subject: [PATCH 01/12] --- updated-dependencies: - dependency-name: sphinx-click dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 0f8148172..a421e1702 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ sphinx==7.3.7 sphinx_rtd_theme==2.0.0 -sphinx-click==5.1.0 +sphinx-click==6.0.0 click prettytable rich From 6d6ab5ac464ce42e1f0d62d619d67cce549d458b Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Tue, 28 May 2024 17:17:34 -0500 Subject: [PATCH 02/12] Fixed #2154 added more error handling around table output --- SoftLayer/CLI/block/detail.py | 24 ++-- SoftLayer/CLI/file/detail.py | 78 ++++--------- SoftLayer/CLI/formatting.py | 11 +- .../fixtures/SoftLayer_Network_Storage.py | 103 ++++++++++++++++++ tests/CLI/formatting_table_tests.py | 44 ++++++++ tests/CLI/modules/file_tests.py | 8 ++ 6 files changed, 197 insertions(+), 71 deletions(-) diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py index 7752e1b56..263e21b48 100644 --- a/SoftLayer/CLI/block/detail.py +++ b/SoftLayer/CLI/block/detail.py @@ -83,25 +83,21 @@ def cli(env, volume_id): table.add_row(['Replication Status', "%s" % block_volume['replicationStatus']]) + replicant_table = formatting.KeyValueTable(['Name', 'Value']) + replicant_table.align['Name'] = 'r' + replicant_table.align['Value'] = 'l' for replicant in block_volume['replicationPartners']: - replicant_table = formatting.Table(['Name', - 'Value']) - replicant_table.add_row(['Replicant Id', replicant['id']]) replicant_table.add_row([ - 'Volume Name', - utils.lookup(replicant, 'username')]) + 'Replicant Id', replicant['id']]) replicant_table.add_row([ - 'Target IP', - utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) + 'Volume Name', utils.lookup(replicant, 'username')]) replicant_table.add_row([ - 'Data Center', - utils.lookup(replicant, - 'serviceResource', 'datacenter', 'name')]) + 'Target IP', utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) replicant_table.add_row([ - 'Schedule', - utils.lookup(replicant, - 'replicationSchedule', 'type', 'keyname')]) - table.add_row(['Replicant Volumes', replicant_table]) + 'Data Center', utils.lookup(replicant, 'serviceResource', 'datacenter', 'name')]) + replicant_table.add_row([ + 'Schedule', utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname')]) + table.add_row(['Replicant Volumes', replicant_table]) if block_volume.get('originalVolumeSize'): original_volume_info = formatting.Table(['Property', 'Value']) diff --git a/SoftLayer/CLI/file/detail.py b/SoftLayer/CLI/file/detail.py index cc4860452..009dc1fc9 100644 --- a/SoftLayer/CLI/file/detail.py +++ b/SoftLayer/CLI/file/detail.py @@ -37,54 +37,27 @@ def cli(env, volume_id): table.add_row(['Type', storage_type]) table.add_row(['Capacity (GB)', get_capacity(file_volume)]) - used_space = int(file_volume['bytesUsed']) \ - if file_volume['bytesUsed'] else 0 - if used_space < (1 << 10): - table.add_row(['Used Space', "%dB" % used_space]) - elif used_space < (1 << 20): - table.add_row(['Used Space', "%dKB" % (used_space / (1 << 10))]) - elif used_space < (1 << 30): - table.add_row(['Used Space', "%dMB" % (used_space / (1 << 20))]) - else: - table.add_row(['Used Space', "%dGB" % (used_space / (1 << 30))]) + used_space = formatting.convert_sizes(file_volume.get('bytes_used', 0), "GB", False) + table.add_row(['Used Space', used_space]) if file_volume.get('provisionedIops'): table.add_row(['IOPs', float(file_volume['provisionedIops'])]) if file_volume.get('storageTierLevel'): - table.add_row([ - 'Endurance Tier', - file_volume['storageTierLevel'], - ]) - - table.add_row([ - 'Data Center', - file_volume['serviceResource']['datacenter']['name'], - ]) - table.add_row([ - 'Target IP', - file_volume['serviceResourceBackendIpAddress'], - ]) + table.add_row(['Endurance Tier', file_volume['storageTierLevel']]) + + table.add_row(['Data Center', file_volume['serviceResource']['datacenter']['name']]) + table.add_row(['Target IP', file_volume['serviceResourceBackendIpAddress']]) if file_volume['fileNetworkMountAddress']: - table.add_row([ - 'Mount Address', - file_volume['fileNetworkMountAddress'], - ]) + table.add_row(['Mount Address', file_volume['fileNetworkMountAddress']]) if file_volume['snapshotCapacityGb']: - table.add_row([ - 'Snapshot Capacity (GB)', - file_volume['snapshotCapacityGb'], - ]) + table.add_row(['Snapshot Capacity (GB)', file_volume['snapshotCapacityGb']]) if 'snapshotSizeBytes' in file_volume['parentVolume']: - table.add_row([ - 'Snapshot Used (Bytes)', - file_volume['parentVolume']['snapshotSizeBytes'], - ]) + table.add_row(['Snapshot Used (Bytes)', file_volume['parentVolume']['snapshotSizeBytes']]) - table.add_row(['# of Active Transactions', "%i" - % file_volume['activeTransactionCount']]) + table.add_row(["# of Active Transactions", file_volume['activeTransactionCount']]) if file_volume['activeTransactions']: for trans in file_volume['activeTransactions']: @@ -98,32 +71,25 @@ def cli(env, volume_id): # returns a string or object for 'replicationStatus'; it seems that # the type is string for File volumes and object for Block volumes if 'message' in file_volume['replicationStatus']: - table.add_row(['Replication Status', "%s" - % file_volume['replicationStatus']['message']]) + table.add_row(['Replication Status', file_volume['replicationStatus']['message']]) else: - table.add_row(['Replication Status', "%s" - % file_volume['replicationStatus']]) + table.add_row(['Replication Status', file_volume['replicationStatus']]) - replicant_list = [] + replicant_table = formatting.KeyValueTable(['Name', 'Value']) + replicant_table.align['Name'] = 'r' + replicant_table.align['Value'] = 'l' for replicant in file_volume['replicationPartners']: - replicant_table = formatting.Table(['Replicant ID', - replicant['id']]) replicant_table.add_row([ - 'Volume Name', - utils.lookup(replicant, 'username')]) + 'Volume ID', replicant.get('id')]) + replicant_table.add_row([ + 'Volume Name', utils.lookup(replicant, 'username')]) replicant_table.add_row([ - 'Target IP', - utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) + 'Target IP', utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) replicant_table.add_row([ - 'Data Center', - utils.lookup(replicant, - 'serviceResource', 'datacenter', 'name')]) + 'Data Center', utils.lookup(replicant, 'serviceResource', 'datacenter', 'name')]) replicant_table.add_row([ - 'Schedule', - utils.lookup(replicant, - 'replicationSchedule', 'type', 'keyname')]) - replicant_list.append(replicant_table) - table.add_row(['Replicant Volumes', replicant_list]) + 'Schedule', utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname')]) + table.add_row(['Replicant Volumes', replicant_table]) if file_volume.get('originalVolumeSize'): original_volume_info = formatting.Table(['Property', 'Value']) diff --git a/SoftLayer/CLI/formatting.py b/SoftLayer/CLI/formatting.py index b8c6e4a8f..c4c284636 100644 --- a/SoftLayer/CLI/formatting.py +++ b/SoftLayer/CLI/formatting.py @@ -14,6 +14,7 @@ import click from rich import box +from rich.errors import NotRenderableError from rich.table import Table as rTable from SoftLayer.CLI import exceptions @@ -392,7 +393,15 @@ def prettytable(self, fmt='table', theme=None): table.add_column(col, justify=justify, style=style) for row in self.rows: - table.add_row(*row) + try: + table.add_row(*row) + # Generally you will see this if one of the columns in the row is a list or dict + except NotRenderableError: + forced_row = [] + for i in row: + forced_row.append(str(i)) + table.add_row(*forced_row) + return table diff --git a/SoftLayer/fixtures/SoftLayer_Network_Storage.py b/SoftLayer/fixtures/SoftLayer_Network_Storage.py index 2d9cdc499..d9e927bd8 100644 --- a/SoftLayer/fixtures/SoftLayer_Network_Storage.py +++ b/SoftLayer/fixtures/SoftLayer_Network_Storage.py @@ -334,4 +334,107 @@ 'storageType': { 'keyName': 'ENDURANCE_BLOCK_STORAGE' } +} + + +FILE_DETAIL_ISSUE2154 = { + "capacityGb": 150, + "id": 609491933, + "username": "SL02SV1414935_187", + "activeTransactionCount": 0, + "replicationPartnerCount": 1, + "fileNetworkMountAddress": "fsf-natestdal0505-fcb-fz.service.softlayer.com:/SL02SV1414935_187/data01", + "originalVolumeSize": "20", + "provisionedIops": "2000", + "replicationStatus": "FAILOVER_COMPLETED", + "serviceResourceBackendIpAddress": "fsf-natestdal0505-fcb-fz.service.softlayer.com", + "snapshotCapacityGb": "5", + "activeTransactions": [ + { + "createDate": "", + "elapsedSeconds": 111763, + "guestId": "", + "hardwareId": "", + "id": "", + "modifyDate": "", + "statusChangeDate": "", + "transactionGroup": { + "name": "Volume Modification" + }, + "transactionStatus": { + "friendlyName": "In Progress" + } + } + ], + "parentVolume": { + "accountId": 1414935, + "capacityGb": 120, + "createDate": "2024-05-16T02:28:02-05:00", + "guestId": "", + "hardwareId": "", + "hostId": "", + "id": 609491967, + "nasType": "SNAPSHOT", + "notes": "vol_duplicate_snapshot_2024-05-16_0228", + "serviceProviderId": 1, + "storageTypeId": "16", + "upgradableFlag": True, + "username": "SL02SV1414935_187", + "serviceResourceBackendIpAddress": "fsf-natestdal0505-fcb-fz.service.softlayer.com", + "serviceResourceName": "Storage Type 02 Aggregate natestdal0505-fc-d", + "snapshotSizeBytes": "0" + }, + "replicationPartners": [ + { + "id": 609491945, + "username": "SL02SV1414935_187_REP_1", + "serviceResourceBackendIpAddress": "fsf-natestdal0505-ffb-fz.service.softlayer.com", + "replicationSchedule": { + "active": 1, + "createDate": "2024-05-16T01:20:19-05:00", + "id": 666339, + "modifyDate": "", + "name": "SL02SV1414935_187_HOURLY_REP", + "partnershipId": "", + "typeId": 32, + "volumeId": 609491933, + "type": { + "keyname": "REPLICATION_HOURLY" + } + }, + "serviceResource": { + "backendIpAddress": "fsf-natestdal0505-ffb-fz.service.softlayer.com", + "id": 57365, + "name": "Storage Type 02 Aggregate natestdal0505-ff-d", + "datacenter": { + "name": "dal10" + }, + "type": { + "type": "NETAPP_STOR_AGGR" + } + } + } + ], + "serviceResource": { + "backendIpAddress": "fsf-natestdal0505-fcb-fz.service.softlayer.com", + "id": 52292, + "name": "Storage Type 02 Aggregate natestdal0505-fc-d", + "attributes": [ + { + "value": "2", + "attributeType": { + "keyname": "STAAS_VERSION" + } + } + ], + "datacenter": { + "name": "lon02" + }, + "type": { + "type": "NETAPP_STOR_AGGR" + } + }, + "storageType": { + "keyName": "PERFORMANCE_FILE_STORAGE" } +} diff --git a/tests/CLI/formatting_table_tests.py b/tests/CLI/formatting_table_tests.py index 117667072..4d62a742b 100644 --- a/tests/CLI/formatting_table_tests.py +++ b/tests/CLI/formatting_table_tests.py @@ -4,6 +4,8 @@ :license: MIT, see LICENSE for more details. """ +from rich.console import Console + from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @@ -21,6 +23,48 @@ def test_boolean_table(self): table.add_row(["entry1"]) self.assertTrue(table) + def test_key_value_table(self): + expected = """┌───────────┬─────────────────────────┐ +│ Key │ Value │ +├───────────┼─────────────────────────┤ +│ First │ One │ +│ Sub Table │ ┌─────────┬───────────┐ │ +│ │ │ Sub Key │ Sub Value │ │ +│ │ ├─────────┼───────────┤ │ +│ │ │ Second │ Two │ │ +│ │ └─────────┴───────────┘ │ +└───────────┴─────────────────────────┘ +""" + table = formatting.KeyValueTable(["Key", "Value"]) + table.add_row(["First", "One"]) + sub_table = formatting.KeyValueTable(["Sub Key", "Sub Value"]) + sub_table.add_row(["Second", "Two"]) + table.add_row(["Sub Table", sub_table]) + console = Console() + + with console.capture() as capture: + to_print = formatting.format_output(table) + console.print(to_print) + result = capture.get() + self.assertEqual(expected, result) + + def test_unrenderable_recovery_table(self): + expected = """│ Sub Table │ [ Date: Tue, 28 May 2024 18:10:54 -0500 Subject: [PATCH 03/12] Fixed up some unit tests --- SoftLayer/CLI/block/detail.py | 59 +++------ SoftLayer/CLI/exceptions.py | 15 ++- SoftLayer/CLI/file/detail.py | 19 ++- SoftLayer/testing/__init__.py | 1 + tests/CLI/modules/block_tests.py | 52 ++++---- tests/CLI/modules/file_tests.py | 197 ++++++++++++------------------- 6 files changed, 145 insertions(+), 198 deletions(-) diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py index 263e21b48..a4359fae3 100644 --- a/SoftLayer/CLI/block/detail.py +++ b/SoftLayer/CLI/block/detail.py @@ -31,72 +31,51 @@ def cli(env, volume_id): table.add_row(['Username', block_volume['username']]) table.add_row(['Type', storage_type]) table.add_row(['Capacity (GB)', capacity]) - table.add_row(['LUN Id', "%s" % block_volume['lunId']]) + table.add_row(['LUN Id', block_volume['lunId']]) if block_volume.get('provisionedIops'): - table.add_row(['IOPs', float(block_volume['provisionedIops'])]) + table.add_row(['IOPs', block_volume['provisionedIops']]) if block_volume.get('storageTierLevel'): - table.add_row([ - 'Endurance Tier', - block_volume['storageTierLevel'], - ]) - - table.add_row([ - 'Data Center', - block_volume['serviceResource']['datacenter']['name'], - ]) - table.add_row([ - 'Target IP', - block_volume['serviceResourceBackendIpAddress'], - ]) + table.add_row(['Endurance Tier', block_volume['storageTierLevel']]) + + table.add_row(['Data Center', block_volume['serviceResource']['datacenter']['name']]) + table.add_row(['Target IP', block_volume['serviceResourceBackendIpAddress']]) if block_volume['snapshotCapacityGb']: - table.add_row([ - 'Snapshot Capacity (GB)', - block_volume['snapshotCapacityGb'], - ]) + table.add_row(['Snapshot Capacity (GB)', block_volume['snapshotCapacityGb']]) if 'snapshotSizeBytes' in block_volume['parentVolume']: - table.add_row([ - 'Snapshot Used (Bytes)', - block_volume['parentVolume']['snapshotSizeBytes'], - ]) + table.add_row(['Snapshot Used (Bytes)', block_volume['parentVolume']['snapshotSizeBytes']]) - table.add_row(['# of Active Transactions', "%i" - % block_volume['activeTransactionCount']]) + table.add_row(['# of Active Transactions', block_volume['activeTransactionCount']]) if block_volume['activeTransactions']: for trans in block_volume['activeTransactions']: if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']: table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']]) - table.add_row(['Replicant Count', "%u" % block_volume.get('replicationPartnerCount', 0)]) + table.add_row(['Replicant Count', block_volume.get('replicationPartnerCount', 0)]) if block_volume['replicationPartnerCount'] > 0: # This if/else temporarily handles a bug in which the SL API # returns a string or object for 'replicationStatus'; it seems that # the type is string for File volumes and object for Block volumes if 'message' in block_volume['replicationStatus']: - table.add_row(['Replication Status', "%s" - % block_volume['replicationStatus']['message']]) + table.add_row(['Replication Status', block_volume['replicationStatus']['message']]) else: - table.add_row(['Replication Status', "%s" - % block_volume['replicationStatus']]) + table.add_row(['Replication Status', block_volume['replicationStatus']]) - replicant_table = formatting.KeyValueTable(['Name', 'Value']) + replicant_table = formatting.Table(['Id', 'Username', 'Target', 'Location', 'Schedule']) replicant_table.align['Name'] = 'r' replicant_table.align['Value'] = 'l' for replicant in block_volume['replicationPartners']: replicant_table.add_row([ - 'Replicant Id', replicant['id']]) - replicant_table.add_row([ - 'Volume Name', utils.lookup(replicant, 'username')]) - replicant_table.add_row([ - 'Target IP', utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) - replicant_table.add_row([ - 'Data Center', utils.lookup(replicant, 'serviceResource', 'datacenter', 'name')]) - replicant_table.add_row([ - 'Schedule', utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname')]) + replicant.get('id'), + utils.lookup(replicant, 'username'), + utils.lookup(replicant, 'serviceResourceBackendIpAddress'), + utils.lookup(replicant, 'serviceResource', 'datacenter', 'name'), + utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname') + ]) table.add_row(['Replicant Volumes', replicant_table]) if block_volume.get('originalVolumeSize'): diff --git a/SoftLayer/CLI/exceptions.py b/SoftLayer/CLI/exceptions.py index 611d854ea..55acdb60e 100644 --- a/SoftLayer/CLI/exceptions.py +++ b/SoftLayer/CLI/exceptions.py @@ -16,9 +16,8 @@ def __init__(self, code=0, *args): self.code = code def __str__(self): - return "" % (self.code, - getattr(self, 'message')) - + message = getattr(self, 'message') + return f"" __repr__ = __str__ @@ -29,6 +28,11 @@ def __init__(self, msg, *args): super().__init__(code=2, *args) self.message = msg + def __str__(self): + message = getattr(self, 'message') + return f"" + __repr__ = __str__ + class ArgumentError(CLIAbort): """Halt the execution of the command because of invalid arguments.""" @@ -36,3 +40,8 @@ class ArgumentError(CLIAbort): def __init__(self, msg, *args): super().__init__(msg, *args) self.message = "Argument Error: %s" % msg + + def __str__(self): + message = getattr(self, 'message') + return f"" + __repr__ = __str__ diff --git a/SoftLayer/CLI/file/detail.py b/SoftLayer/CLI/file/detail.py index 009dc1fc9..ca12e43a3 100644 --- a/SoftLayer/CLI/file/detail.py +++ b/SoftLayer/CLI/file/detail.py @@ -41,7 +41,7 @@ def cli(env, volume_id): table.add_row(['Used Space', used_space]) if file_volume.get('provisionedIops'): - table.add_row(['IOPs', float(file_volume['provisionedIops'])]) + table.add_row(['IOPs', file_volume['provisionedIops']]) if file_volume.get('storageTierLevel'): table.add_row(['Endurance Tier', file_volume['storageTierLevel']]) @@ -75,20 +75,17 @@ def cli(env, volume_id): else: table.add_row(['Replication Status', file_volume['replicationStatus']]) - replicant_table = formatting.KeyValueTable(['Name', 'Value']) + replicant_table = formatting.Table(['Id', 'Username', 'Target', 'Location', 'Schedule']) replicant_table.align['Name'] = 'r' replicant_table.align['Value'] = 'l' for replicant in file_volume['replicationPartners']: replicant_table.add_row([ - 'Volume ID', replicant.get('id')]) - replicant_table.add_row([ - 'Volume Name', utils.lookup(replicant, 'username')]) - replicant_table.add_row([ - 'Target IP', utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) - replicant_table.add_row([ - 'Data Center', utils.lookup(replicant, 'serviceResource', 'datacenter', 'name')]) - replicant_table.add_row([ - 'Schedule', utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname')]) + replicant.get('id'), + utils.lookup(replicant, 'username'), + utils.lookup(replicant, 'serviceResourceBackendIpAddress'), + utils.lookup(replicant, 'serviceResource', 'datacenter', 'name'), + utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname') + ]) table.add_row(['Replicant Volumes', replicant_table]) if file_volume.get('originalVolumeSize'): diff --git a/SoftLayer/testing/__init__.py b/SoftLayer/testing/__init__.py index 6eff9851c..b55ea2e86 100644 --- a/SoftLayer/testing/__init__.py +++ b/SoftLayer/testing/__init__.py @@ -111,6 +111,7 @@ def setUp(self): # NOQA self.env = environment.Environment() self.env.client = self.client self.set_up() + self.maxDiff = None def tearDown(self): # NOQA super().tearDown() diff --git a/tests/CLI/modules/block_tests.py b/tests/CLI/modules/block_tests.py index 5a7cd7c84..d21c8dece 100644 --- a/tests/CLI/modules/block_tests.py +++ b/tests/CLI/modules/block_tests.py @@ -65,10 +65,9 @@ def test_volume_detail(self): self.assert_called_with('SoftLayer_Network_Storage', 'getObject', identifier=1234) self.assertEqual({ 'Username': 'username', - 'LUN Id': '2', - 'Notes': "{'status': 'available'}", 'Endurance Tier': 'READHEAVY_TIER', - 'IOPs': 1000.0, + 'IOPs': "1000", + 'LUN Id': 2, 'Snapshot Capacity (GB)': '10', 'Snapshot Used (Bytes)': 1024, 'Capacity (GB)': '20GB', @@ -76,24 +75,31 @@ def test_volume_detail(self): 'Data Center': 'dal05', 'Type': 'ENDURANCE', 'ID': 100, - '# of Active Transactions': '1', + 'Notes': "{'status': 'available'}", + '# of Active Transactions': 1, 'Ongoing Transaction': 'This is a buffer time in which the customer may cancel the server', - 'Replicant Count': '1', - 'Replication Status': 'Replicant Volume Provisioning ' - 'has completed.', - 'Replicant Volumes': [ - {'Name': 'Replicant Id', 'Value': 1785}, - {'Name': 'Volume Name', 'Value': 'TEST_REP_2'}, - {'Name': 'Target IP', 'Value': '10.3.177.84'}, - {'Name': 'Data Center', 'Value': 'dal01'}, - {'Name': 'Schedule', 'Value': 'REPLICATION_DAILY'}], + 'Replicant Count': 1, + 'Replication Status': 'Replicant Volume Provisioning has completed.', + "Replicant Volumes": [ + { + "Id": 1784, + "Username": "TEST_REP_1", + "Target": "10.3.174.79", + "Location": "wdc01", + "Schedule": "REPLICATION_HOURLY" + }, + { + "Id": 1785, + "Username": "TEST_REP_2", + "Target": "10.3.177.84", + "Location": "dal01", + "Schedule": "REPLICATION_DAILY" + } + ], 'Original Volume Properties': [ - {'Property': 'Original Volume Size', - 'Value': '20'}, - {'Property': 'Original Volume Name', - 'Value': 'test-original-volume-name'}, - {'Property': 'Original Snapshot Name', - 'Value': 'test-original-snapshot-name'} + {'Property': 'Original Volume Size', 'Value': '20'}, + {'Property': 'Original Volume Name', 'Value': 'test-original-volume-name'}, + {'Property': 'Original Snapshot Name', 'Value': 'test-original-snapshot-name'} ] }, json.loads(result.output)) @@ -102,10 +108,10 @@ def test_block_detail_issue1732(self): lun_mock.return_value = SoftLayer_Network_Storage.BLOCK_LIST_ISSUES_1732 result = self.run_command(['--format=table', 'block', 'volume-detail', '1234']) self.assert_no_fail(result) - self.assertIn('│ Username │ SL02SEL307608-60 │', result.output) - self.assertIn('│ Capacity (GB) │ 16000GB │', result.output) - self.assertIn('│ Replication Status │ FAILBACK_COMPLETED │', result.output) - self.assertIn('│ Notes │ test │', result.output) + self.assertIn('Username │ SL02SEL307608-60', result.output) + self.assertIn('Capacity (GB) │ 16000GB', result.output) + self.assertIn('Replication Status │ FAILBACK_COMPLETED', result.output) + self.assertIn('Notes │ test', result.output) def test_volume_detail_name_identifier(self): result = self.run_command(['block', 'volume-detail', 'SL-12345']) diff --git a/tests/CLI/modules/file_tests.py b/tests/CLI/modules/file_tests.py index 19fc7ffe2..c68ac7a08 100644 --- a/tests/CLI/modules/file_tests.py +++ b/tests/CLI/modules/file_tests.py @@ -163,11 +163,12 @@ def test_volume_detail(self): result = self.run_command(['file', 'volume-detail', '1234']) self.assert_no_fail(result) + print(result.output) self.assertEqual({ 'Username': 'username', - 'Used Space': '0B', + 'Used Space': '0.00 MB', 'Endurance Tier': 'READHEAVY_TIER', - 'IOPs': 1000, + 'IOPs': "1000", 'Mount Address': '127.0.0.1:/TEST', 'Snapshot Capacity (GB)': '10', 'Snapshot Used (Bytes)': 1024, @@ -177,29 +178,30 @@ def test_volume_detail(self): 'Type': 'ENDURANCE', 'ID': 100, 'Notes': "{'status': 'available'}", - '# of Active Transactions': '1', + '# of Active Transactions': 1, 'Ongoing Transaction': 'This is a buffer time in which the customer may cancel the server', 'Replicant Count': '1', - 'Replication Status': 'Replicant Volume Provisioning ' - 'has completed.', - 'Replicant Volumes': [[ - {'Replicant ID': 'Volume Name', '1784': 'TEST_REP_1'}, - {'Replicant ID': 'Target IP', '1784': '10.3.174.79'}, - {'Replicant ID': 'Data Center', '1784': 'wdc01'}, - {'Replicant ID': 'Schedule', '1784': 'REPLICATION_HOURLY'}, - ], [ - {'Replicant ID': 'Volume Name', '1785': 'TEST_REP_2'}, - {'Replicant ID': 'Target IP', '1785': '10.3.177.84'}, - {'Replicant ID': 'Data Center', '1785': 'dal01'}, - {'Replicant ID': 'Schedule', '1785': 'REPLICATION_DAILY'}, - ]], + 'Replication Status': 'Replicant Volume Provisioning has completed.', + "Replicant Volumes": [ + { + "Id": 1784, + "Username": "TEST_REP_1", + "Target": "10.3.174.79", + "Location": "wdc01", + "Schedule": "REPLICATION_HOURLY" + }, + { + "Id": 1785, + "Username": "TEST_REP_2", + "Target": "10.3.177.84", + "Location": "dal01", + "Schedule": "REPLICATION_DAILY" + } + ], 'Original Volume Properties': [ - {'Property': 'Original Volume Size', - 'Value': '20'}, - {'Property': 'Original Volume Name', - 'Value': 'test-original-volume-name'}, - {'Property': 'Original Snapshot Name', - 'Value': 'test-original-snapshot-name'} + {'Property': 'Original Volume Size', 'Value': '20'}, + {'Property': 'Original Volume Name', 'Value': 'test-original-volume-name'}, + {'Property': 'Original Snapshot Name', 'Value': 'test-original-snapshot-name'} ] }, json.loads(result.output)) @@ -229,17 +231,14 @@ def test_volume_detail_issues2154(self): self.assertIn("SL02SV1414935_187", result.output) def test_volume_order_performance_iops_not_given(self): - result = self.run_command(['file', 'volume-order', - '--storage-type=performance', '--size=20', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=performance', '--size=20', '--location=dal05']) self.assertEqual(2, result.exit_code) def test_volume_order_performance_snapshot_error(self): - result = self.run_command(['file', 'volume-order', - '--storage-type=performance', '--size=20', - '--iops=100', '--location=dal05', - '--snapshot-size=10', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=performance', '--size=20', + '--iops=100', '--location=dal05', '--snapshot-size=10', '--service-offering=performance']) self.assertEqual(2, result.exit_code) @@ -258,10 +257,8 @@ def test_volume_order_performance(self, order_mock): } } - result = self.run_command(['file', 'volume-order', - '--storage-type=performance', '--size=20', - '--iops=100', '--location=dal05', - '--snapshot-size=10']) + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=performance', '--size=20', + '--iops=100', '--location=dal05', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, @@ -273,8 +270,7 @@ def test_volume_order_performance(self, order_mock): 'ready.\n') def test_volume_order_endurance_tier_not_given(self): - result = self.run_command(['file', 'volume-order', - '--storage-type=endurance', '--size=20', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=endurance', '--size=20', '--location=dal05']) self.assertEqual(2, result.exit_code) @@ -293,10 +289,8 @@ def test_volume_order_endurance(self, order_mock): } } - result = self.run_command(['file', 'volume-order', - '--storage-type=endurance', '--size=20', - '--tier=0.25', '--location=dal05', - '--snapshot-size=10']) + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=endurance', '--size=20', + '--tier=0.25', '--location=dal05', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, @@ -311,20 +305,17 @@ def test_volume_order_endurance(self, order_mock): def test_volume_order_order_not_placed(self, order_mock): order_mock.return_value = {} - result = self.run_command(['file', 'volume-order', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal05']) self.assert_no_fail(result) self.assertEqual(result.output, - 'Order could not be placed! Please verify ' - 'your options and try again.\n') + 'Order could not be placed! Please verify your options and try again.\n') def test_volume_order_hourly_billing_not_available(self): - result = self.run_command(['file', 'volume-order', - '--storage-type=endurance', '--size=20', - '--tier=0.25', '--location=dal10', - '--billing=hourly', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=endurance', '--size=20', + '--tier=0.25', '--location=dal10', '--billing=hourly', '--service-offering=enterprise']) self.assertEqual(2, result.exit_code) @@ -343,10 +334,8 @@ def test_volume_order_hourly_billing(self, order_mock): } } - result = self.run_command(['file', 'volume-order', - '--storage-type=endurance', '--size=20', - '--tier=0.25', '--location=dal05', - '--service-offering=storage_as_a_service', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=endurance', '--size=20', + '--tier=0.25', '--location=dal05', '--service-offering=storage_as_a_service', '--billing=hourly', '--snapshot-size=10']) self.assert_no_fail(result) @@ -364,39 +353,35 @@ def test_volume_order_hourly_billing(self, order_mock): def test_volume_order_performance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') - result = self.run_command(['file', 'volume-order', - '--storage-type=performance', '--size=20', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--location=dal05']) self.assertEqual(2, result.exit_code) + print(result.output) self.assertEqual('Argument Error: failure!', result.exception.message) @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_endurance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') - result = self.run_command(['file', 'volume-order', - '--storage-type=endurance', '--size=20', + result = self.run_command(['--really', 'file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal05']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) def test_enable_snapshots(self): - result = self.run_command(['file', 'snapshot-enable', '12345678', - '--schedule-type=HOURLY', '--minute=10', + result = self.run_command(['file', 'snapshot-enable', '12345678', '--schedule-type=HOURLY', '--minute=10', '--retention-count=5']) self.assert_no_fail(result) def test_disable_snapshots(self): - result = self.run_command(['file', 'snapshot-disable', '12345678', - '--schedule-type=HOURLY']) + result = self.run_command(['file', 'snapshot-disable', '12345678', '--schedule-type=HOURLY']) self.assert_no_fail(result) def test_list_volume_schedules(self): - result = self.run_command([ - 'file', 'snapshot-schedule-list', '12345678']) + result = self.run_command(['file', 'snapshot-schedule-list', '12345678']) self.assert_no_fail(result) self.assertEqual([ { @@ -465,20 +450,17 @@ def test_delete_snapshot(self): def test_snapshot_order_order_not_placed(self, order_mock): order_mock.return_value = {} - result = self.run_command(['file', 'snapshot-order', '1234', - '--capacity=10', '--tier=0.25']) + result = self.run_command(['--really', 'file', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assert_no_fail(result) self.assertEqual(result.output, - 'Order could not be placed! Please verify ' - 'your options and try again.\n') + 'Order could not be placed! Please verify your options and try again.\n') @mock.patch('SoftLayer.FileStorageManager.order_snapshot_space') def test_snapshot_order_performance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') - result = self.run_command(['file', 'snapshot-order', '1234', - '--capacity=10', '--tier=0.25']) + result = self.run_command(['--really', 'file', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) @@ -494,8 +476,7 @@ def test_snapshot_order(self, order_mock): } } - result = self.run_command(['file', 'snapshot-order', '1234', - '--capacity=10', '--tier=0.25']) + result = self.run_command(['--really', 'file', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assert_no_fail(result) self.assertEqual(result.output, @@ -504,32 +485,25 @@ def test_snapshot_order(self, order_mock): ' > Order status: PENDING_APPROVAL\n') def test_snapshot_cancel(self): - result = self.run_command(['--really', - 'file', 'snapshot-cancel', '1234']) + result = self.run_command(['--really', 'file', 'snapshot-cancel', '1234']) self.assert_no_fail(result) - self.assertEqual('File volume with id 1234 has been marked' - ' for snapshot cancellation\n', result.output) - self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', - args=(False, True, None)) + self.assertEqual('File volume with id 1234 has been marked for snapshot cancellation\n', result.output) + self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, None)) def test_replicant_failover(self): - result = self.run_command(['file', 'replica-failover', '12345678', - '--replicant-id=5678']) + result = self.run_command(['file', 'replica-failover', '12345678', '--replicant-id=5678']) self.assert_no_fail(result) - self.assertEqual('Failover to replicant is now in progress.\n', - result.output) + self.assertEqual('Failover to replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.failover_to_replicant') def test_replicant_failover_unsuccessful(self, failover_mock): failover_mock.return_value = False - result = self.run_command(['file', 'replica-failover', '12345678', - '--replicant-id=5678']) + result = self.run_command(['file', 'replica-failover', '12345678', '--replicant-id=5678']) - self.assertEqual('Failover operation could not be initiated.\n', - result.output) + self.assertEqual('Failover operation could not be initiated.\n', result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') @mock.patch('SoftLayer.FileStorageManager.disaster_recovery_failover_to_replicant') @@ -545,8 +519,7 @@ def test_disaster_recovery_failover(self, disaster_recovery_failover_mock, confi def test_disaster_recovery_failover_aborted(self, confirm_mock): confirm_mock.return_value = False - result = self.run_command(['file', 'disaster-recovery-failover', '12345678', - '--replicant-id=5678']) + result = self.run_command(['file', 'disaster-recovery-failover', '12345678', '--replicant-id=5678']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @@ -555,8 +528,7 @@ def test_replicant_failback(self): result = self.run_command(['file', 'replica-failback', '12345678']) self.assert_no_fail(result) - self.assertEqual('Failback from replicant is now in progress.\n', - result.output) + self.assertEqual('Failback from replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.failback_from_replicant') def test_replicant_failback_unsuccessful(self, failback_mock): @@ -564,21 +536,18 @@ def test_replicant_failback_unsuccessful(self, failback_mock): result = self.run_command(['file', 'replica-failback', '12345678']) - self.assertEqual('Failback operation could not be initiated.\n', - result.output) + self.assertEqual('Failback operation could not be initiated.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.order_replicant_volume') def test_replicant_order_order_not_placed(self, order_mock): order_mock.return_value = {} - result = self.run_command(['file', 'replica-order', '100', - '--snapshot-schedule=DAILY', + result = self.run_command(['--really', 'file', 'replica-order', '100', '--snapshot-schedule=DAILY', '--location=dal05']) self.assert_no_fail(result) self.assertEqual(result.output, - 'Order could not be placed! Please verify ' - 'your options and try again.\n') + 'Order could not be placed! Please verify your options and try again.\n') @mock.patch('SoftLayer.FileStorageManager.order_replicant_volume') def test_replicant_order(self, order_mock): @@ -596,9 +565,8 @@ def test_replicant_order(self, order_mock): } } - result = self.run_command(['file', 'replica-order', '100', - '--snapshot-schedule=DAILY', - '--location=dal05', '--tier=2']) + result = self.run_command(['--really', 'file', 'replica-order', '100', + '--snapshot-schedule=DAILY', '--location=dal05', '--tier=2']) self.assert_no_fail(result) self.assertEqual(result.output, @@ -613,19 +581,14 @@ def test_replicant_order(self, order_mock): def test_replication_locations(self): result = self.run_command(['file', 'replica-locations', '1234']) self.assert_no_fail(result) - self.assertEqual( - { - '12345': 'Dallas 05', - }, - json.loads(result.output)) + self.assertEqual({'12345': 'Dallas 05'}, json.loads(result.output)) @mock.patch('SoftLayer.FileStorageManager.get_replication_locations') def test_replication_locations_unsuccessful(self, locations_mock): locations_mock.return_value = False result = self.run_command(['file', 'replica-locations', '1234']) self.assert_no_fail(result) - self.assertEqual('No data centers compatible for replication.\n', - result.output) + self.assertEqual('No data centers compatible for replication.\n', result.output) def test_replication_partners(self): result = self.run_command(['file', 'replica-partners', '1234']) @@ -663,23 +626,19 @@ def test_replication_partners_unsuccessful(self, partners_mock): def test_duplicate_order_exception_caught(self, order_mock): order_mock.side_effect = ValueError('order attempt failed, oh noooo!') - result = self.run_command(['file', 'volume-duplicate', '100']) + result = self.run_command(['--really', 'file', 'volume-duplicate', '100']) self.assertEqual(2, result.exit_code) - self.assertEqual('Argument Error: order attempt failed, oh noooo!', - result.exception.message) + self.assertEqual('Argument Error: order attempt failed, oh noooo!', result.exception.message) @mock.patch('SoftLayer.FileStorageManager.order_duplicate_volume') def test_duplicate_order_order_not_placed(self, order_mock): order_mock.return_value = {} - result = self.run_command(['file', 'volume-duplicate', '100', - '--duplicate-iops=1400']) + result = self.run_command(['--really', 'file', 'volume-duplicate', '100', '--duplicate-iops=1400']) self.assert_no_fail(result) - self.assertEqual(result.output, - 'Order could not be placed! Please verify ' - 'your options and try again.\n') + self.assertEqual(result.output, 'Order could not be placed! Please verify your options and try again.\n') @mock.patch('SoftLayer.FileStorageManager.order_duplicate_volume') def test_duplicate_order(self, order_mock): @@ -690,11 +649,9 @@ def test_duplicate_order(self, order_mock): } } - result = self.run_command(['file', 'volume-duplicate', '100', - '--origin-snapshot-id=470', - '--duplicate-size=250', - '--duplicate-tier=2', - '--duplicate-snapshot-size=20']) + result = self.run_command(['--really', 'file', 'volume-duplicate', '100', + '--origin-snapshot-id=470', '--duplicate-size=250', + '--duplicate-tier=2', '--duplicate-snapshot-size=20']) self.assert_no_fail(result) self.assertEqual(result.output, @@ -710,10 +667,8 @@ def test_duplicate_order_hourly_billing(self, order_mock): } } - result = self.run_command(['file', 'volume-duplicate', '100', - '--origin-snapshot-id=470', - '--duplicate-size=250', - '--duplicate-tier=2', '--billing=hourly', + result = self.run_command(['--really', 'file', 'volume-duplicate', '100', '--origin-snapshot-id=470', + '--duplicate-size=250', '--duplicate-tier=2', '--billing=hourly', '--duplicate-snapshot-size=20']) order_mock.assert_called_with('100', origin_snapshot_id=470, @@ -731,7 +686,7 @@ def test_duplicate_order_hourly_billing(self, order_mock): def test_modify_order_exception_caught(self, order_mock): order_mock.side_effect = ValueError('order attempt failed, noooo!') - result = self.run_command(['file', 'volume-modify', '102', '--new-size=1000']) + result = self.run_command(['--really', 'file', 'volume-modify', '102', '--new-size=1000']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: order attempt failed, noooo!', result.exception.message) @@ -740,7 +695,7 @@ def test_modify_order_exception_caught(self, order_mock): def test_modify_order_order_not_placed(self, order_mock): order_mock.return_value = {} - result = self.run_command(['file', 'volume-modify', '102', '--new-iops=1400']) + result = self.run_command(['--really', 'file', 'volume-modify', '102', '--new-iops=1400']) self.assert_no_fail(result) self.assertEqual('Order could not be placed! Please verify your options and try again.\n', result.output) @@ -751,7 +706,7 @@ def test_modify_order(self, order_mock): {'description': '1000 GBs'}, {'description': '4 IOPS per GB'}]}} - result = self.run_command(['file', 'volume-modify', '102', '--new-size=1000', '--new-tier=4']) + result = self.run_command(['--really', 'file', 'volume-modify', '102', '--new-size=1000', '--new-tier=4']) order_mock.assert_called_with('102', new_size=1000, new_iops=None, new_tier_level=4) self.assert_no_fail(result) From ed42da56ba9b8968c9d23ae34973b5de70dd1d3f Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Thu, 6 Jun 2024 15:51:30 -0500 Subject: [PATCH 04/12] Reworked user permissions list to include permissions by department and be more accurate with new permissions being added #2156 --- SoftLayer/CLI/user/permissions.py | 29 +- .../SoftLayer_User_Permission_Department.py | 978 ++++++++++++++++++ SoftLayer/managers/user.py | 16 +- tests/CLI/modules/user_tests.py | 2 +- tests/managers/user_tests.py | 7 + 5 files changed, 1018 insertions(+), 14 deletions(-) create mode 100644 SoftLayer/fixtures/SoftLayer_User_Permission_Department.py diff --git a/SoftLayer/CLI/user/permissions.py b/SoftLayer/CLI/user/permissions.py index 4aaeeb12e..7f8fd06eb 100644 --- a/SoftLayer/CLI/user/permissions.py +++ b/SoftLayer/CLI/user/permissions.py @@ -1,4 +1,4 @@ -"""List A users permissions.""" +"""List a users permissions.""" import click import SoftLayer @@ -11,21 +11,31 @@ @click.argument('identifier') @environment.pass_env def cli(env, identifier): - """User Permissions.""" + """User Permissions. + + Some permissions here may also be managed by IBM IAM service. + See https://cloud.ibm.com/docs/account?topic=account-migrated_permissions for more details. + """ mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'username') object_mask = "mask[id, permissions, isMasterUserFlag, roles]" user = mgr.get_user(user_id, object_mask) - all_permissions = mgr.get_all_permissions() - user_permissions = perms_to_dict(user['permissions']) + all_permissions = mgr.get_permission_departments() + user_permissions = perms_to_dict(user['permissions']) + all_table = formatting.KeyValueTable(['Department', 'Permissions']) if user['isMasterUserFlag']: click.secho('This account is the Master User and has all permissions enabled', fg='green') env.fout(roles_table(user)) - env.fout(permission_table(user_permissions, all_permissions)) + for department in all_permissions: + all_table.add_row([ + department.get('name'), + permission_table(user_permissions, department.get('permissions', [])) + ]) + env.fout(all_table) def perms_to_dict(perms): @@ -39,18 +49,13 @@ def perms_to_dict(perms): def permission_table(user_permissions, all_permissions): """Creates a table of available permissions""" - table = formatting.Table(['Description', 'KeyName', 'Assigned']) + table = formatting.Table(['KeyName', 'Assigned', 'Description']) table.align['KeyName'] = 'l' table.align['Description'] = 'l' table.align['Assigned'] = 'l' for perm in all_permissions: assigned = user_permissions.get(perm['keyName'], False) - hide_permission_list = ['ACCOUNT_SUMMARY_VIEW', 'REQUEST_COMPLIANCE_REPORT', - 'COMPANY_EDIT', 'ONE_TIME_PAYMENTS', 'UPDATE_PAYMENT_DETAILS', - 'EU_LIMITED_PROCESSING_MANAGE', 'TICKET_ADD', 'TICKET_EDIT', - 'TICKET_SEARCH', 'TICKET_VIEW', 'TICKET_VIEW_ALL'] - if perm['keyName'] not in hide_permission_list: - table.add_row([perm['name'], perm['keyName'], assigned]) + table.add_row([perm['keyName'], assigned, perm['description']]) return table diff --git a/SoftLayer/fixtures/SoftLayer_User_Permission_Department.py b/SoftLayer/fixtures/SoftLayer_User_Permission_Department.py new file mode 100644 index 000000000..0510ff2a4 --- /dev/null +++ b/SoftLayer/fixtures/SoftLayer_User_Permission_Department.py @@ -0,0 +1,978 @@ +getAllObjects = [ + { + "description": "Administrative", + "id": 1, + "keyName": "ADMINISTRATIVE", + "name": "Administrative", + "permissions": [ + { + "createDate": None, + "departmentId": 1, + "description": "Permission to access account billing system type determination endpoint", + "id": 5088, + "key": None, + "keyName": "ACCOUNT_BILLING_SYSTEM", + "modifyDate": None, + "name": "Account Billing System" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Activate Partner Customer Account", + "id": 1315, + "key": "A_16", + "keyName": "ACTIVATE_PARTNER_ACCOUNT", + "modifyDate": None, + "name": "Activate Partner Customer Account" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Permission to create sub brands", + "id": 1313, + "key": "A_13", + "keyName": "ACCOUNT_BRAND_ADD", + "modifyDate": None, + "name": "Add Brand Account" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Permission to create customer account.", + "id": 1312, + "key": "A_14", + "keyName": "ACCOUNT_CUSTOMER_ADD", + "modifyDate": None, + "name": "Add Customer Account" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Permission to interface with the Automated Brand Migration process", + "id": 5089, + "key": None, + "keyName": "AUTOMATED_BRAND_MIGRATION", + "modifyDate": None, + "name": "Automated Brand Migration" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Modify the account and company profile associated with this account.", + "id": 1268, + "key": "A_2", + "keyName": "COMPANY_EDIT", + "modifyDate": None, + "name": "Edit Company Profile" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Permission to manage account notes", + "id": 5087, + "key": None, + "keyName": "MANAGE_ACCOUNT_NOTE", + "modifyDate": None, + "name": "Manage Account Notes" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Manage e-mail delivery service accounts.", + "id": 1308, + "key": "NET_4", + "keyName": "NETWORK_MESSAGE_DELIVERY_MANAGE", + "modifyDate": None, + "name": "Manage E-mail Delivery Service" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Provides users ability to toggle the EU Supported account flag.", + "id": 3693, + "key": "EU_1", + "keyName": "EU_LIMITED_PROCESSING_MANAGE", + "modifyDate": None, + "name": "Manage EU Supported Account Flag" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Create and manage notification subscribers for usage warnings and overages.", + "id": 1296, + "key": "NTF_1", + "keyName": "NTF_SUBSCRIBER_MANAGE", + "modifyDate": None, + "name": "Manage Notification Subscribers" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Manage users and assign permissions.", + "id": 1266, + "key": "A_0", + "keyName": "USER_MANAGE", + "modifyDate": None, + "name": "Manage Users" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Physically Access a Customer's Colo Cage", + "id": 1319, + "key": "DA_2", + "keyName": "DATACENTER_ROOM_ACCESS", + "modifyDate": None, + "name": "Physically Access a Customer's Colo Cage" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Physically Access a Datacenter", + "id": 1318, + "key": "DA_1", + "keyName": "DATACENTER_ACCESS", + "modifyDate": None, + "name": "Physically Access a Datacenter" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Submit one-time payments for this account.", + "id": 63, + "key": "A_4", + "keyName": "ONE_TIME_PAYMENTS", + "modifyDate": None, + "name": "Submit One-Time Payments" + }, + { + "createDate": None, + "departmentId": 1, + "description": "Update the recurring monthly payment information.", + "id": 62, + "key": "A_3", + "keyName": "UPDATE_PAYMENT_DETAILS", + "modifyDate": None, + "name": "Update Payment Details" + }, + { + "createDate": None, + "departmentId": 1, + "description": "View the account summary page including invoices and payments.", + "id": 64, + "key": "A_1", + "keyName": "ACCOUNT_SUMMARY_VIEW", + "modifyDate": None, + "name": "View Account Summary" + }, + { + "createDate": None, + "departmentId": 1, + "description": "View the account-wide event log history.", + "id": 1314, + "key": "A_15", + "keyName": "USER_EVENT_LOG_VIEW", + "modifyDate": None, + "name": "View Event Log" + } + ] + }, + { + "description": "Sales", + "id": 2, + "keyName": "SALES", + "name": "Sales", + "permissions": [ + { + "createDate": None, + "departmentId": 2, + "description": "Add new servers to the account.", + "id": 1267, + "key": "XX_1", + "keyName": "SERVER_ADD", + "modifyDate": None, + "name": "Add Server" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Add and upgrade any cloud computing instances on the account.", + "id": 1303, + "key": "A_11", + "keyName": "INSTANCE_UPGRADE", + "modifyDate": None, + "name": "Add/Upgrade Cloud Instances" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Add and upgrade any services on the account.", + "id": 1271, + "key": "A_7", + "keyName": "SERVICE_ADD", + "modifyDate": None, + "name": "Add/Upgrade Services" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Add and upgrade any storage services (StorageLayer) on the account.", + "id": 1265, + "key": "A_10", + "keyName": "ADD_SERVICE_STORAGE", + "modifyDate": None, + "name": "Add/Upgrade Storage (StorageLayer)" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Cancel any servers on the account.", + "id": 1270, + "key": "A_6", + "keyName": "SERVER_CANCEL", + "modifyDate": None, + "name": "Cancel Server" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Cancel any services on the account.", + "id": 1273, + "key": "A_9", + "keyName": "SERVICE_CANCEL", + "modifyDate": None, + "name": "Cancel Services" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Upgrade any servers on the account.", + "id": 1269, + "key": "A_5", + "keyName": "SERVER_UPGRADE", + "modifyDate": None, + "name": "Upgrade Server" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Upgrade Services", + "id": 1272, + "key": "A_8", + "keyName": "SERVICE_UPGRADE", + "modifyDate": None, + "name": "Upgrade Services" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Permission to view billing ACH information.", + "id": 5085, + "key": None, + "keyName": "VIEW_ACH_INFO", + "modifyDate": None, + "name": "View Billing ACH Information" + }, + { + "createDate": None, + "departmentId": 2, + "description": "Permission to view an order with reseller pricing.", + "id": 5086, + "key": None, + "keyName": "VIEW_RESELLER_ORDER", + "modifyDate": None, + "name": "View reseller order pricing" + } + ] + }, + { + "description": "Support", + "id": 3, + "keyName": "SUPPORT", + "name": "Support", + "permissions": [ + { + "createDate": None, + "departmentId": 3, + "description": "Add new support tickets.", + "id": 3, + "key": "T_7", + "keyName": "TICKET_ADD", + "modifyDate": None, + "name": "Add Tickets" + }, + { + "createDate": None, + "departmentId": 3, + "description": "Edit support tickets.", + "id": 2, + "key": "T_8", + "keyName": "TICKET_EDIT", + "modifyDate": None, + "name": "Edit Tickets" + }, + { + "createDate": None, + "departmentId": 3, + "description": "Search through previous tickets.", + "id": 203, + "key": "T_2", + "keyName": "TICKET_SEARCH", + "modifyDate": None, + "name": "Search Tickets" + }, + { + "createDate": None, + "departmentId": 3, + "description": "View all tickets regardless of which user the ticket is assigned to.", + "id": 1321, + "key": "T_6", + "keyName": "TICKET_VIEW_ALL", + "modifyDate": None, + "name": "View All Tickets" + }, + { + "createDate": None, + "departmentId": 3, + "description": "View tickets assigned to the user.", + "id": 1, + "key": "T_1", + "keyName": "TICKET_VIEW", + "modifyDate": None, + "name": "View Tickets" + } + ] + }, + { + "description": "Security", + "id": 4, + "keyName": "SECURITY", + "name": "Security", + "permissions": [ + { + "createDate": None, + "departmentId": 4, + "description": "Add, remove, and update certificates (includes the private key).", + "id": 1325, + "key": "SE_9", + "keyName": "SECURITY_CERTIFICATE_MANAGE", + "modifyDate": None, + "name": "Manage Certificates (SSL)" + }, + { + "createDate": None, + "departmentId": 4, + "description": "Create, edit and delete SAML authentication records.", + "id": 3664, + "key": "SM_1", + "keyName": "SAML_AUTHENTICATION_MANAGE", + "modifyDate": None, + "name": "Manage SAML Authentication" + }, + { + "createDate": None, + "departmentId": 4, + "description": "Add, remove, and update SSH keys.", + "id": 1320, + "key": "SE_10", + "keyName": "CUSTOMER_SSH_KEY_MANAGEMENT", + "modifyDate": None, + "name": "Manage SSH Keys" + }, + { + "createDate": None, + "departmentId": 4, + "description": "Request compliance reports.", + "id": 2442, + "key": "COM_1", + "keyName": "REQUEST_COMPLIANCE_REPORT", + "modifyDate": None, + "name": "Request Compliance Report" + }, + { + "createDate": None, + "departmentId": 4, + "description": "View certificates (includes the private key).", + "id": 1324, + "key": "SE_8", + "keyName": "SECURITY_CERTIFICATE_VIEW", + "modifyDate": None, + "name": "View Certificates (SSL)" + }, + { + "createDate": None, + "departmentId": 4, + "description": "Request and view Vulnerability Scans.", + "id": 1295, + "key": "SE_7", + "keyName": "VULN_SCAN_MANAGE", + "modifyDate": None, + "name": "Vulnerability Scanning" + } + ] + }, + { + "description": "Devices", + "id": 5, + "keyName": "DEVICES", + "name": "Devices", + "permissions": [ + { + "createDate": None, + "departmentId": 5, + "description": "Allows a user to access virtual dedicated hosts", + "id": 3679, + "key": "ALL_3", + "keyName": "ACCESS_ALL_DEDICATEDHOSTS", + "modifyDate": None, + "name": "Access Virtual DedicatedHosts" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Add IP Addresses to a server.", + "id": 1281, + "key": "H_6", + "keyName": "IP_ADD", + "modifyDate": None, + "name": "Add IP Addresses" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allow a user to access all guests on the account.", + "id": 1841, + "key": "ALL_2", + "keyName": "ACCESS_ALL_GUEST", + "modifyDate": None, + "name": "All Guest Access" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allow a user to access all hardware on the account.", + "id": 1821, + "key": "ALL_1", + "keyName": "ACCESS_ALL_HARDWARE", + "modifyDate": None, + "name": "All Hardware Access" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Edit hostname and domain name for devices on the account.", + "id": 1304, + "key": "H_7", + "keyName": "HOSTNAME_EDIT", + "modifyDate": None, + "name": "Edit Hostname/Domain" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allows users to edit Hardware Component Hard Drive Dirty Attribute.", + "id": 3742, + "key": None, + "keyName": "HARDWARE_COMPONENT_DRIVE_DIRTY_ATTRIBUTE_EDIT", + "modifyDate": None, + "name": "Hardware Component Hard Drive Dirty Attribute Edit" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View Host IDS logs.", + "id": 1294, + "key": "SE_6", + "keyName": "HOST_ID_MANAGE", + "modifyDate": None, + "name": "Host IDS" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View IPMI details regarding hardware and issue reboot commands through the portal.", + "id": 1277, + "key": "H_2", + "keyName": "REMOTE_MANAGEMENT", + "modifyDate": None, + "name": "IPMI Remote Management" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allows users to manage configuration template.", + "id": 3748, + "key": None, + "keyName": "MANAGE_CONFIGURATION_TEMPLATE", + "modifyDate": None, + "name": "Manage Configuration Template" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allows users to manage customer hardware.", + "id": 3746, + "key": None, + "keyName": "MANAGE_CUSTOMER_HARDWARE", + "modifyDate": None, + "name": "Manage Customer Hardware" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View and edit monitoring information for devices.", + "id": 1278, + "key": "H_3", + "keyName": "MONITORING_MANAGE", + "modifyDate": None, + "name": "Manage Device Monitoring" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Manage Customer Post Provisioning Scripts.", + "id": 541, + "key": "SO_8", + "keyName": "CUSTOMER_POST_PROVISION_SCRIPT_MANAGEMENT", + "modifyDate": None, + "name": "Manage Provisioning Scripts" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Manage Public Image Templates.", + "id": 1323, + "key": "I_1", + "keyName": "PUBLIC_IMAGE_MANAGE", + "modifyDate": None, + "name": "Manage Public Images" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Issue OS reloads and Rescue Kernel for devices.", + "id": 1279, + "key": "H_4", + "keyName": "SERVER_RELOAD", + "modifyDate": None, + "name": "OS Reloads and Rescue Kernel" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View storage details and edit storage passwords.", + "id": 1283, + "key": "NAS_2", + "keyName": "NAS_MANAGE", + "modifyDate": None, + "name": "Storage Manage" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View hardware information such as IP addresses, OS type, p", + "id": 163, + "key": "H_1", + "keyName": "HARDWARE_VIEW", + "modifyDate": None, + "name": "View Hardware Details" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allows users to view location reservation.", + "id": 3750, + "key": None, + "keyName": "VIEW_LOCATION_RESERVATION", + "modifyDate": None, + "name": "View Location Reservation" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View virtual dedicated host information. ", + "id": 3684, + "key": "VH_1", + "keyName": "DEDICATED_HOST_VIEW", + "modifyDate": None, + "name": "View Virtual Dedicated Host Details" + }, + { + "createDate": None, + "departmentId": 5, + "description": "View virtual server information such as IP addresses, OS type, passwords, e", + "id": 1302, + "key": "VG_1", + "keyName": "VIRTUAL_GUEST_VIEW", + "modifyDate": None, + "name": "View Virtual Server Details" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allows users to view and edit dedicated host.", + "id": 6278, + "key": None, + "keyName": "MANAGE_DEDICATED_HOST", + "modifyDate": None, + "name": "View and edit dedicated host" + }, + { + "createDate": None, + "departmentId": 5, + "description": "Allows users to view and edit virtual guest data.", + "id": 6280, + "key": None, + "keyName": "MANAGE_VIRTUAL_GUEST", + "modifyDate": None, + "name": "View and edit virtual guest" + } + ] + }, + { + "description": "Network", + "id": 6, + "keyName": "NETWORK", + "name": "Network", + "permissions": [ + { + "createDate": None, + "departmentId": 6, + "description": "When adding compute (Server or Cloud Instance), a", + "id": 3682, + "key": "NET_6", + "keyName": "PUBLIC_NETWORK_COMPUTE", + "modifyDate": None, + "name": "Add Compute with Public Network Port" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage content delivery network account.", + "id": 1298, + "key": "CDN_1", + "keyName": "CDN_ACCOUNT_MANAGE", + "modifyDate": None, + "name": "Manage CDN Account" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage content delivery network file transfers.", + "id": 1299, + "key": "CDN_2", + "keyName": "CDN_FILE_MANAGE", + "modifyDate": None, + "name": "Manage CDN File Transfers" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Add, edit, and view DNS records managed by SoftLayer.", + "id": 1275, + "key": "DNS_1", + "keyName": "DNS_MANAGE", + "modifyDate": None, + "name": "Manage DNS" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage all firewall rules.", + "id": 1322, + "key": "FW_1", + "keyName": "FIREWALL_RULE_MANAGE", + "modifyDate": None, + "name": "Manage Firewall Rules" + }, + { + "createDate": None, + "departmentId": 6, + "description": "View and edit firewall logs and settings.", + "id": 1290, + "key": "SE_2", + "keyName": "FIREWALL_MANAGE", + "modifyDate": None, + "name": "Manage Firewalls" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage IPSEC network tunnels.", + "id": 250, + "key": "NET_3", + "keyName": "NETWORK_TUNNEL_MANAGE", + "modifyDate": None, + "name": "Manage IPSEC Network Tunnels" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage load balancers.", + "id": 1289, + "key": "LBS_1", + "keyName": "LOADBALANCER_MANAGE", + "modifyDate": None, + "name": "Manage Load Balancers" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage network gateway appliances.", + "id": 1842, + "key": "GTW_1", + "keyName": "GATEWAY_MANAGE", + "modifyDate": None, + "name": "Manage Network Gateways" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage network IDs", + "id": 1293, + "key": "SE_5", + "keyName": "NETWORK_IDS_MANAGE", + "modifyDate": None, + "name": "Manage Network IDs" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage network subnet routes.", + "id": 1301, + "key": "NET_1", + "keyName": "NETWORK_ROUTE_MANAGE", + "modifyDate": None, + "name": "Manage Network Subnet Routes" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Enable and disable private network VLAN spanning.", + "id": 1297, + "key": "NET_2", + "keyName": "NETWORK_VLAN_SPANNING", + "modifyDate": None, + "name": "Manage Network VLAN Spanning" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage port status and speeds for connected devices.", + "id": 1285, + "key": "PO_1", + "keyName": "PORT_CONTROL", + "modifyDate": None, + "name": "Manage Port Control" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Permission to connect and disconnect account with the private endpoint service.", + "id": 5048, + "key": None, + "keyName": "MANAGE_PRIVATE_ENDPOINT_SERVICE", + "modifyDate": None, + "name": "Manage Private Endpoint Service" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Permission to Manage the Public Network", + "id": 3672, + "key": "MP_1", + "keyName": "MANAGE_PUBLIC_NETWORK", + "modifyDate": None, + "name": "Manage Public Network" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage security groups.", + "id": 3678, + "key": "NET_5", + "keyName": "MANAGE_SECURITY_GROUPS", + "modifyDate": None, + "name": "Manage Security Groups" + }, + { + "createDate": None, + "departmentId": 6, + "description": "Manage VPN access for all users.", + "id": 1316, + "key": "VPN_1", + "keyName": "VPN_MANAGE", + "modifyDate": None, + "name": "VPN Administration" + }, + { + "createDate": None, + "departmentId": 6, + "description": "View bandwidth statistics and graphs for hardware.", + "id": 1274, + "key": "B_1", + "keyName": "BANDWIDTH_MANAGE", + "modifyDate": None, + "name": "View Bandwidth Statistics" + }, + { + "createDate": None, + "departmentId": 6, + "description": "View content delivery network bandwidth statistics.", + "id": 1300, + "key": "CDN_3", + "keyName": "CDN_BANDWIDTH_VIEW", + "modifyDate": None, + "name": "View CDN Bandwidth Statistics" + } + ] + }, + { + "description": "Software", + "id": 7, + "keyName": "SOFTWARE", + "name": "Software", + "permissions": [ + { + "createDate": None, + "departmentId": 7, + "description": "View and edit antivirus / spyware logs and settings.", + "id": 1292, + "key": "SE_4", + "keyName": "ANTI_MALWARE_MANAGE", + "modifyDate": None, + "name": "Manage Antivirus/Spyware" + }, + { + "createDate": None, + "departmentId": 7, + "description": "Manage firewall software", + "id": 1291, + "key": "SE_3", + "keyName": "SOFTWARE_FIREWALL_MANAGE", + "modifyDate": None, + "name": "Manage Firewall Software" + }, + { + "createDate": None, + "departmentId": 7, + "description": "Permission to initiate and delete an openstack link.", + "id": 3514, + "key": None, + "keyName": "OPENSTACK_LINK", + "modifyDate": None, + "name": "Openstack Link" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View Customer Software Password", + "id": 240, + "key": "SO_9", + "keyName": "VIEW_CUSTOMER_SOFTWARE_PASSWORD", + "modifyDate": None, + "name": "View Customer Software Password" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View login information for Helm.", + "id": 1263, + "key": "SO_3", + "keyName": "VIEW_HELM", + "modifyDate": None, + "name": "View Helm" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View login information for Plesk.", + "id": 1262, + "key": "SO_2", + "keyName": "VIEW_PLESK", + "modifyDate": None, + "name": "View Plesk" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View login information for QuantaStor.", + "id": 1317, + "key": "SO_7", + "keyName": "VIEW_QUANTASTOR", + "modifyDate": None, + "name": "View QuantaStor" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View login information for Urchin.", + "id": 1264, + "key": "SO_4", + "keyName": "VIEW_URCHIN", + "modifyDate": None, + "name": "View Urchin" + }, + { + "createDate": None, + "departmentId": 7, + "description": "Allows users to view and edit disk image data.", + "id": 6279, + "key": None, + "keyName": "MANAGE_DISK_IMAGE", + "modifyDate": None, + "name": "View and edit disk image" + }, + { + "createDate": None, + "departmentId": 7, + "description": "Allows users to view and edit image template.", + "id": 6277, + "key": None, + "keyName": "IMAGE_TEMPLATE_MANAGE", + "modifyDate": None, + "name": "View and edit manage image template" + }, + { + "createDate": None, + "departmentId": 7, + "description": "Allows users to view and edit software component.", + "id": 6276, + "key": None, + "keyName": "SOFTWARE_MANAGE", + "modifyDate": None, + "name": "View and edit software component" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View login information for cPanel.", + "id": 1261, + "key": "SO_1", + "keyName": "VIEW_CPANEL", + "modifyDate": None, + "name": "View cPanel" + }, + { + "createDate": None, + "departmentId": 7, + "description": "View licenses", + "id": 1280, + "key": "H_5", + "keyName": "LICENSE_VIEW", + "modifyDate": None, + "name": "View licenses" + }, + { + "createDate": None, + "departmentId": 7, + "description": "Allows users to view software account license.", + "id": 6275, + "key": None, + "keyName": "SOFTWARE_LICENSE_MANAGE", + "modifyDate": None, + "name": "View software account license" + } + ] + } +] diff --git a/SoftLayer/managers/user.py b/SoftLayer/managers/user.py index 8e854426c..72b23dfda 100644 --- a/SoftLayer/managers/user.py +++ b/SoftLayer/managers/user.py @@ -15,6 +15,7 @@ LOGGER = logging.getLogger(__name__) +# pylint: disable=too-many-public-methods class UserManager(utils.IdentifierMixin, object): """Manages Users. @@ -76,7 +77,7 @@ def get_current_user(self, objectmask=None): return self.account_service.getCurrentUser(mask=objectmask) def get_all_permissions(self): - """Calls SoftLayer_User_CustomerPermissions_Permission::getAllObjects + """Calls User_Permission_Action::getAllObjects Stores the result in self.all_permissions :returns: A list of dictionaries that contains all valid permissions @@ -86,6 +87,19 @@ def get_all_permissions(self): self.all_permissions = sorted(permissions, key=itemgetter('keyName')) return self.all_permissions + def get_permission_departments(self): + """Calls SoftLayer_User_Permission_Department::getAllObjects + + Stores the result in self.all_permissions + :returns: A list of dictionaries that contains all valid permissions + """ + mask = "mask[permissions[id,keyName,description,name]]" + departments = self.client.call('User_Permission_Department', 'getAllObjects', mask=mask) + for i, department in enumerate(departments): + departments[i]['permissions'] = sorted(department.get('permissions'), key=itemgetter('keyName')) + + return departments + def get_all_notifications(self): """Calls SoftLayer_Email_Subscription::getAllObjects diff --git a/tests/CLI/modules/user_tests.py b/tests/CLI/modules/user_tests.py index 804c9ef1d..514d662ac 100644 --- a/tests/CLI/modules/user_tests.py +++ b/tests/CLI/modules/user_tests.py @@ -113,7 +113,7 @@ def test_print_hardware_access(self): def test_permissions_list(self): result = self.run_command(['user', 'permissions', '11100']) self.assert_no_fail(result) - self.assert_called_with('SoftLayer_User_Permission_Action', 'getAllObjects') + self.assert_called_with('SoftLayer_User_Permission_Department', 'getAllObjects') self.assert_called_with( 'SoftLayer_User_Customer', 'getObject', identifier=11100, mask='mask[id, permissions, isMasterUserFlag, roles]' diff --git a/tests/managers/user_tests.py b/tests/managers/user_tests.py index b5b5f9da1..f129e0c10 100644 --- a/tests/managers/user_tests.py +++ b/tests/managers/user_tests.py @@ -364,3 +364,10 @@ def test_get_api_authentication_keys(self): def test_remove_api_authentication_key(self): self.manager.remove_api_authentication_key(123456) self.assert_called_with('SoftLayer_User_Customer', 'removeApiAuthenticationKey') + + def test_get_permission_departments(self): + result = self.manager.get_permission_departments() + self.assert_called_with('SoftLayer_User_Permission_Department', 'getAllObjects') + # just making sure the lists are sorted. + self.assertEqual(result[0]['permissions'][0]['keyName'], 'ACCOUNT_BILLING_SYSTEM') + self.assertEqual(result[1]['permissions'][8]['keyName'], 'VIEW_ACH_INFO') From 576ab309a287ea7a3ff7e51163d884e8435aa8cb Mon Sep 17 00:00:00 2001 From: d3rn <84808889+d3rnn@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:55:11 -0500 Subject: [PATCH 05/12] Update list.py --- SoftLayer/CLI/virt/list.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SoftLayer/CLI/virt/list.py b/SoftLayer/CLI/virt/list.py index b16d5602f..b350f75bc 100644 --- a/SoftLayer/CLI/virt/list.py +++ b/SoftLayer/CLI/virt/list.py @@ -21,7 +21,8 @@ column_helper.Column('action', lambda guest: formatting.active_txn(guest), mask='activeTransaction[id,transactionStatus[name,friendlyName]]'), column_helper.Column('power_state', ('powerState', 'name')), - column_helper.Column('created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), + column_helper.Column('created_by', lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column('tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), column_helper.Column( From c1789ff0fc84dc8c2f653ad86b75359fbefd0f3e Mon Sep 17 00:00:00 2001 From: d3rn <84808889+d3rnn@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:55:47 -0500 Subject: [PATCH 06/12] Update list.py --- SoftLayer/CLI/hardware/list.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SoftLayer/CLI/hardware/list.py b/SoftLayer/CLI/hardware/list.py index 42b298155..0dae57a11 100644 --- a/SoftLayer/CLI/hardware/list.py +++ b/SoftLayer/CLI/hardware/list.py @@ -8,6 +8,7 @@ from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers +from SoftLayer import utils # pylint: disable=unnecessary-lambda @@ -22,7 +23,8 @@ mask='activeTransaction[id, transactionStatus[name, friendlyName]]'), column_helper.Column( 'created_by', - ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), + lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column( 'tags', lambda server: formatting.tags(server.get('tagReferences')), From 6581ed122b7e40cd0f2f13de41583e3700632833 Mon Sep 17 00:00:00 2001 From: d3rn <84808889+d3rnn@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:56:15 -0500 Subject: [PATCH 07/12] Update list_guests.py --- SoftLayer/CLI/dedicatedhost/list_guests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SoftLayer/CLI/dedicatedhost/list_guests.py b/SoftLayer/CLI/dedicatedhost/list_guests.py index 6d263941d..bf00a236c 100644 --- a/SoftLayer/CLI/dedicatedhost/list_guests.py +++ b/SoftLayer/CLI/dedicatedhost/list_guests.py @@ -8,6 +8,7 @@ from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers +from SoftLayer import utils COLUMNS = [ column_helper.Column('guid', ('globalIdentifier',)), @@ -18,7 +19,8 @@ column_helper.Column('backend_ip', ('primaryBackendIpAddress',)), column_helper.Column( 'created_by', - ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), + lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column('power_state', ('powerState', 'name')), column_helper.Column( 'tags', From 5c411a61e4b0d6ffda39651a86003e131a55f6db Mon Sep 17 00:00:00 2001 From: d3rnn <84808889+d3rnn@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:24:30 +0000 Subject: [PATCH 08/12] fix E131 and E501 --- SoftLayer/CLI/dedicatedhost/list_guests.py | 4 ++-- SoftLayer/CLI/hardware/list.py | 4 ++-- SoftLayer/CLI/virt/list.py | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/SoftLayer/CLI/dedicatedhost/list_guests.py b/SoftLayer/CLI/dedicatedhost/list_guests.py index bf00a236c..5d8f2c3bc 100644 --- a/SoftLayer/CLI/dedicatedhost/list_guests.py +++ b/SoftLayer/CLI/dedicatedhost/list_guests.py @@ -19,8 +19,8 @@ column_helper.Column('backend_ip', ('primaryBackendIpAddress',)), column_helper.Column( 'created_by', - lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), - mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), + lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column('power_state', ('powerState', 'name')), column_helper.Column( 'tags', diff --git a/SoftLayer/CLI/hardware/list.py b/SoftLayer/CLI/hardware/list.py index 0dae57a11..734f379d4 100644 --- a/SoftLayer/CLI/hardware/list.py +++ b/SoftLayer/CLI/hardware/list.py @@ -23,8 +23,8 @@ mask='activeTransaction[id, transactionStatus[name, friendlyName]]'), column_helper.Column( 'created_by', - lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), - mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), + lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column( 'tags', lambda server: formatting.tags(server.get('tagReferences')), diff --git a/SoftLayer/CLI/virt/list.py b/SoftLayer/CLI/virt/list.py index b350f75bc..b491c471e 100644 --- a/SoftLayer/CLI/virt/list.py +++ b/SoftLayer/CLI/virt/list.py @@ -21,7 +21,8 @@ column_helper.Column('action', lambda guest: formatting.active_txn(guest), mask='activeTransaction[id,transactionStatus[name,friendlyName]]'), column_helper.Column('power_state', ('powerState', 'name')), - column_helper.Column('created_by', lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + column_helper.Column('created_by', + lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column('tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), From 3adf56cdee1194f22736db3057d7a99e17d8d900 Mon Sep 17 00:00:00 2001 From: d3rnn <84808889+d3rnn@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:30:23 +0000 Subject: [PATCH 09/12] fix W291 and E501 --- SoftLayer/CLI/virt/list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SoftLayer/CLI/virt/list.py b/SoftLayer/CLI/virt/list.py index b491c471e..d30b4ff6a 100644 --- a/SoftLayer/CLI/virt/list.py +++ b/SoftLayer/CLI/virt/list.py @@ -21,8 +21,8 @@ column_helper.Column('action', lambda guest: formatting.active_txn(guest), mask='activeTransaction[id,transactionStatus[name,friendlyName]]'), column_helper.Column('power_state', ('powerState', 'name')), - column_helper.Column('created_by', - lambda created_by: utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), + column_helper.Column('created_by', lambda created_by: + utils.lookup(created_by, 'billingItem', 'orderItem', 'order', 'userRecord', 'username'), mask='billingItem[id,orderItem[id,order[id,userRecord[username]]]]'), column_helper.Column('tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), From 071683b73e970d1e3ba75220219bee933b91ebf7 Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Fri, 12 Jul 2024 13:39:14 -0500 Subject: [PATCH 10/12] Updated CodeQL Jobs, Fixed #2036 --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index adc5408b6..dc02956df 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -43,7 +43,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -68,4 +68,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 53d3871bb0a3a644efbf7ee02a05d2b0f106faba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:27:20 +0000 Subject: [PATCH 11/12] pip prod(deps): bump sphinx from 7.3.7 to 7.4.4 Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.3.7 to 7.4.4. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.3.7...v7.4.4) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index a421e1702..36adedad3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==7.3.7 +sphinx==7.4.4 sphinx_rtd_theme==2.0.0 sphinx-click==6.0.0 click From 2899f68f0740bc10a95a3139f87b0999a2fd9f78 Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Wed, 17 Jul 2024 16:12:18 -0500 Subject: [PATCH 12/12] v6.2.3 version bump --- SoftLayer/consts.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SoftLayer/consts.py b/SoftLayer/consts.py index 2bc9bd20a..02cc3352e 100644 --- a/SoftLayer/consts.py +++ b/SoftLayer/consts.py @@ -5,7 +5,7 @@ :license: MIT, see LICENSE for more details. """ -VERSION = 'v6.2.2' +VERSION = 'v6.2.3' API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3.1/' API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3.1/' API_PUBLIC_ENDPOINT_REST = 'https://api.softlayer.com/rest/v3.1/' diff --git a/setup.py b/setup.py index d4490fe20..fd008d57f 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='SoftLayer', - version='v6.2.2', + version='v6.2.3', description=DESCRIPTION, long_description=LONG_DESCRIPTION, long_description_content_type='text/x-rst',