diff --git a/SoftLayer/CLI/block/count.py b/SoftLayer/CLI/block/count.py new file mode 100644 index 000000000..52211f0d5 --- /dev/null +++ b/SoftLayer/CLI/block/count.py @@ -0,0 +1,55 @@ +"""List number of block storage volumes per datacenter.""" +# :license: MIT, see LICENSE for more details. + +import click +import SoftLayer +from SoftLayer.CLI import columns as column_helper +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting + +COLUMNS = [ + column_helper.Column('Datacenter', + ('serviceResource', 'datacenter', 'name'), + mask="serviceResource.datacenter.name"), + column_helper.Column('Count', + '', + mask=None) +] + +DEFAULT_COLUMNS = [ + 'Datacenter', + 'Count' +] + + +@click.command() +@click.option('--datacenter', '-d', help='Datacenter shortname') +@click.option('--sortby', help='Column to sort by', default='Datacenter') +@click.option('--columns', + callback=column_helper.get_formatter(COLUMNS), + help='Columns to display. Options: {0}'.format( + ', '.join(column.name for column in COLUMNS)), + default=','.join(DEFAULT_COLUMNS)) +@environment.pass_env +def cli(env, sortby, columns, datacenter): + """List number of block storage volumes per datacenter.""" + block_manager = SoftLayer.BlockStorageManager(env.client) + block_volumes = block_manager.list_block_volumes(datacenter=datacenter, + mask=columns.mask()) + + # cycle through all block volumes and count datacenter occurences. + datacenters = dict() + for volume in block_volumes: + service_resource = volume['serviceResource'] + if 'datacenter' in service_resource: + datacenter = service_resource['datacenter']['name'] + if datacenter not in datacenters.keys(): + datacenters[datacenter] = 1 + else: + datacenters[datacenter] += 1 + + table = formatting.KeyValueTable(columns.columns) + table.sortby = sortby + for datacenter in datacenters: + table.add_row([datacenter, datacenters[datacenter]]) + env.fout(table) diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py index 51a1fbd4f..c0d1ad371 100644 --- a/SoftLayer/CLI/block/detail.py +++ b/SoftLayer/CLI/block/detail.py @@ -16,7 +16,6 @@ def cli(env, volume_id): block_manager = SoftLayer.BlockStorageManager(env.client) block_volume = block_manager.get_block_volume_details(volume_id) block_volume = utils.NestedDict(block_volume) - table = formatting.KeyValueTable(['Name', 'Value']) table.align['Name'] = 'r' table.align['Value'] = 'l' diff --git a/SoftLayer/CLI/block/list.py b/SoftLayer/CLI/block/list.py index 41e09ca30..4cc9afd2b 100644 --- a/SoftLayer/CLI/block/list.py +++ b/SoftLayer/CLI/block/list.py @@ -28,6 +28,8 @@ column_helper.Column('lunId', ('lunId',), mask="lunId"), column_helper.Column('active_transactions', ('activeTransactionCount',), mask="activeTransactionCount"), + column_helper.Column('rep_partner_count', ('replicationPartnerCount',), + mask="replicationPartnerCount"), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), @@ -42,7 +44,8 @@ 'bytes_used', 'ip_addr', 'lunId', - 'active_transactions' + 'active_transactions', + 'rep_partner_count' ] diff --git a/SoftLayer/CLI/file/count.py b/SoftLayer/CLI/file/count.py new file mode 100644 index 000000000..fc0700cc7 --- /dev/null +++ b/SoftLayer/CLI/file/count.py @@ -0,0 +1,55 @@ +"""List number of file storage volumes per datacenter.""" +# :license: MIT, see LICENSE for more details. + +import click +import SoftLayer +from SoftLayer.CLI import columns as column_helper +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting + +COLUMNS = [ + column_helper.Column('Datacenter', + ('serviceResource', 'datacenter', 'name'), + mask="serviceResource.datacenter.name"), + column_helper.Column('Count', + '', + mask=None) +] + +DEFAULT_COLUMNS = [ + 'Datacenter', + 'Count' +] + + +@click.command() +@click.option('--datacenter', '-d', help='Datacenter shortname') +@click.option('--sortby', help='Column to sort by', default='Datacenter') +@click.option('--columns', + callback=column_helper.get_formatter(COLUMNS), + help='Columns to display. Options: {0}'.format( + ', '.join(column.name for column in COLUMNS)), + default=','.join(DEFAULT_COLUMNS)) +@environment.pass_env +def cli(env, sortby, columns, datacenter): + """List number of file storage volumes per datacenter.""" + + file_manager = SoftLayer.FileStorageManager(env.client) + file_volumes = file_manager.list_file_volumes(datacenter=datacenter, + mask=columns.mask()) + + datacenters = dict() + for volume in file_volumes: + service_resource = volume['serviceResource'] + if 'datacenter' in service_resource: + datacenter = service_resource['datacenter']['name'] + if datacenter not in datacenters.keys(): + datacenters[datacenter] = 1 + else: + datacenters[datacenter] += 1 + + table = formatting.KeyValueTable(columns.columns) + table.sortby = sortby + for datacenter in datacenters: + table.add_row([datacenter, datacenters[datacenter]]) + env.fout(table) diff --git a/SoftLayer/CLI/file/list.py b/SoftLayer/CLI/file/list.py index 40399538c..86028f4ee 100644 --- a/SoftLayer/CLI/file/list.py +++ b/SoftLayer/CLI/file/list.py @@ -29,6 +29,8 @@ mask="activeTransactionCount"), column_helper.Column('mount_addr', ('fileNetworkMountAddress',), mask="fileNetworkMountAddress",), + column_helper.Column('rep_partner_count', ('replicationPartnerCount',), + mask="replicationPartnerCount"), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), @@ -43,7 +45,8 @@ 'bytes_used', 'ip_addr', 'active_transactions', - 'mount_addr' + 'mount_addr', + 'rep_partner_count' ] diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py index 10f2a7da1..ec21e653d 100644 --- a/SoftLayer/CLI/routes.py +++ b/SoftLayer/CLI/routes.py @@ -78,6 +78,7 @@ ('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'), ('block:volume-list', 'SoftLayer.CLI.block.list:cli'), ('block:volume-order', 'SoftLayer.CLI.block.order:cli'), + ('block:volume-count', 'SoftLayer.CLI.block.count:cli'), ('file', 'SoftLayer.CLI.file'), ('file:access-authorize', 'SoftLayer.CLI.file.access.authorize:cli'), @@ -100,6 +101,7 @@ ('file:volume-detail', 'SoftLayer.CLI.file.detail:cli'), ('file:volume-list', 'SoftLayer.CLI.file.list:cli'), ('file:volume-order', 'SoftLayer.CLI.file.order:cli'), + ('file:volume-count', 'SoftLayer.CLI.file.count:cli'), ('firewall', 'SoftLayer.CLI.firewall'), ('firewall:add', 'SoftLayer.CLI.firewall.add:cli'), diff --git a/SoftLayer/fixtures/SoftLayer_Account.py b/SoftLayer/fixtures/SoftLayer_Account.py index 561ff3926..b8005e0ee 100644 --- a/SoftLayer/fixtures/SoftLayer_Account.py +++ b/SoftLayer/fixtures/SoftLayer_Account.py @@ -536,7 +536,7 @@ 'notes': """{'status': 'available'}""", 'password': '', 'serviceProviderId': 1, - 'serviceResource': {'datacenter': {'id': 449500}}, + 'serviceResource': {'datacenter': {'name': 'dal05', 'id': 449500}}, 'serviceResourceBackendIpAddress': '10.1.2.3', 'serviceResourceName': 'Storage Type 01 Aggregate staaspar0101_pc01', 'username': 'username', diff --git a/SoftLayer/managers/block.py b/SoftLayer/managers/block.py index 89660d13e..192605d68 100644 --- a/SoftLayer/managers/block.py +++ b/SoftLayer/managers/block.py @@ -41,10 +41,25 @@ def list_block_volumes(self, datacenter=None, username=None, 'bytesUsed', 'serviceResource.datacenter[name]', 'serviceResourceBackendIpAddress', - 'activeTransactionCount' + 'activeTransactionCount', + 'replicationPartnerCount', + ',replicationPartners[id,username,' + 'serviceResourceBackendIpAddress,' + 'serviceResource[datacenter[name]],' + 'storageType,capacityGb,lunId,bytesUsed,' + 'activeTransactionCount,' + 'replicationSchedule[type[keyname]]]', ] kwargs['mask'] = ','.join(items) + # Retrieve relevant replicant information to be displayed. + kwargs['mask'] += ',replicationPartners[id,username,'\ + 'serviceResourceBackendIpAddress,'\ + 'serviceResource[datacenter[name]],'\ + 'storageType,capacityGb,lunId,bytesUsed,'\ + 'activeTransactionCount,'\ + 'replicationSchedule[type[keyname]]]' + _filter = utils.NestedDict(kwargs.get('filter') or {}) _filter['iscsiNetworkStorage']['serviceResource']['type']['type'] = \ @@ -52,6 +67,7 @@ def list_block_volumes(self, datacenter=None, username=None, _filter['iscsiNetworkStorage']['storageType']['keyName'] = ( utils.query_filter('*BLOCK_STORAGE*')) + if storage_type: _filter['iscsiNetworkStorage']['storageType']['keyName'] = ( utils.query_filter('%s_BLOCK_STORAGE' % storage_type.upper())) diff --git a/SoftLayer/managers/file.py b/SoftLayer/managers/file.py index 22c73b45b..e52a4a0c2 100644 --- a/SoftLayer/managers/file.py +++ b/SoftLayer/managers/file.py @@ -36,10 +36,25 @@ def list_file_volumes(self, datacenter=None, username=None, 'serviceResource.datacenter[name]', 'serviceResourceBackendIpAddress', 'activeTransactionCount', - 'fileNetworkMountAddress' + 'fileNetworkMountAddress', + 'replicationPartnerCount', + ',replicationPartners[id,username,' + 'serviceResourceBackendIpAddress,' + 'serviceResource[datacenter[name]],' + 'storageType,capacityGb,lunId,bytesUsed,' + 'activeTransactionCount,' + 'replicationSchedule[type[keyname]]]', ] kwargs['mask'] = ','.join(items) + # Retrieve relevant replicant information to be displayed. + kwargs['mask'] += ',replicationPartners[id,username,'\ + 'serviceResourceBackendIpAddress,'\ + 'serviceResource[datacenter[name]],'\ + 'storageType,capacityGb,lunId,bytesUsed,'\ + 'activeTransactionCount,'\ + 'replicationSchedule[type[keyname]]]' + _filter = utils.NestedDict(kwargs.get('filter') or {}) _filter['nasNetworkStorage']['serviceResource']['type']['type'] = \ diff --git a/docs/cli.rst b/docs/cli.rst index 1a8613d2c..32249d298 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -58,48 +58,54 @@ To discover the available commands, simply type `slcli`. :: $ slcli - Usage: slcli [OPTIONS] COMMAND [ARGS]... - - SoftLayer Command-line Client - - Options: - --format [table|raw|json] Output format - -C, --config PATH Config file location - --debug [0|1|2|3] Sets the debug noise level - -v, --verbose Sets the debug noise level - --timings Time each API call and display after results - --proxy TEXT HTTP[S] proxy to be use to make API calls - -y, --really Confirm all prompt actions - --fixtures Use fixtures instead of actually making API calls - --version Show the version and exit. - --help Show this message and exit. - - Commands: - call-api Call arbitrary API endpoints. - cdn Content Delivery Network. - config CLI configuration. - dns Domain Name System. - firewall Firewalls. - globalip Global IP addresses. - image Compute images. - iscsi iSCSI storage. - loadbal Load balancers. - messaging Message queue service. - metadata Find details about this machine. - nas Network Attached Storage. - rwhois Referral Whois. - server Hardware servers. - snapshot Snapshots. - sshkey SSH Keys. - ssl SSL Certificates. - subnet Network subnets. - summary Account summary. - ticket Support tickets. - vlan Network VLANs. - vs Virtual Servers. - - To use most commands your SoftLayer username and api_key need to be - configured. The easiest way to do that is to use: 'slcli setup' + Usage: slcli [OPTIONS] COMMAND [ARGS]... + + SoftLayer Command-line Client + + Options: + --format [table|raw|json|jsonraw] + Output format [default: table] + -C, --config PATH Config file location [default: + ~/.softlayer] + -v, --verbose Sets the debug noise level, specify multiple + times for more verbosity. + --proxy TEXT HTTP[S] proxy to be use to make API calls + -y, --really / --not-really Confirm all prompt actions + --demo / --no-demo Use demo data instead of actually making API + calls + --version Show the version and exit. + -h, --help Show this message and exit. + + Commands: + block Block Storage. + call-api Call arbitrary API endpoints. + cdn Content Delivery Network. + config CLI configuration. + dns Domain Name System. + file File Storage. + firewall Firewalls. + globalip Global IP addresses. + hardware Hardware servers. + image Compute images. + loadbal Load balancers. + messaging Message queue service. + metadata Find details about this machine. + nas Network Attached Storage. + object-storage Object Storage. + report Reports. + rwhois Referral Whois. + setup Edit configuration. + shell Enters a shell for slcli. + sshkey SSH Keys. + ssl SSL Certificates. + subnet Network subnets. + summary Account summary. + ticket Support tickets. + virtual Virtual Servers. + vlan Network VLANs. + + To use most commands your SoftLayer username and api_key need to be + configured. The easiest way to do that is to use: 'slcli setup' As you can see, there are a number of commands/sections. To look at the list of subcommands for virtual servers type `slcli vs`. For example: diff --git a/tests/CLI/modules/block_tests.py b/tests/CLI/modules/block_tests.py index f65d1cbc9..101f41e6d 100644 --- a/tests/CLI/modules/block_tests.py +++ b/tests/CLI/modules/block_tests.py @@ -105,16 +105,27 @@ def test_volume_list(self): { 'bytes_used': None, 'capacity_gb': 20, - 'datacenter': None, + 'datacenter': 'dal05', 'id': 100, 'ip_addr': '10.1.2.3', 'lunId': None, + 'rep_partner_count': None, 'storage_type': 'ENDURANCE', 'username': 'username', 'active_transactions': None }], json.loads(result.output)) + def test_volume_count(self): + result = self.run_command(['block', 'volume-count']) + + self.assert_no_fail(result) + self.assertEqual( + { + 'dal05': 1 + }, + json.loads(result.output)) + def test_volume_order_performance_iops_not_given(self): result = self.run_command(['block', 'volume-order', '--storage-type=performance', '--size=20', @@ -357,6 +368,7 @@ def test_replication_locations(self): def test_replication_locations_unsuccessful(self, locations_mock): locations_mock.return_value = False result = self.run_command(['block', 'replica-locations', '1234']) + self.assert_no_fail(result) self.assertEqual('No data centers compatible for replication.\n', result.output) diff --git a/tests/CLI/modules/file_tests.py b/tests/CLI/modules/file_tests.py index df8abec92..cb1eda54d 100644 --- a/tests/CLI/modules/file_tests.py +++ b/tests/CLI/modules/file_tests.py @@ -87,10 +87,21 @@ def test_volume_list(self): 'storage_type': 'ENDURANCE', 'username': 'user', 'active_transactions': None, - 'mount_addr': '127.0.0.1:/TEST' + 'mount_addr': '127.0.0.1:/TEST', + 'rep_partner_count': None }], json.loads(result.output)) + def test_volume_count(self): + result = self.run_command(['block', 'volume-count']) + + self.assert_no_fail(result) + self.assertEqual( + { + 'dal05': 1 + }, + json.loads(result.output)) + def test_snapshot_list(self): result = self.run_command(['file', 'snapshot-list', '1234']) diff --git a/tox.ini b/tox.ini index 6eaaa1f75..61bf0480c 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ basepython = python2.7 deps = -r{toxinidir}/tools/test-requirements.txt hacking - pylint + pylint==1.6.5 commands = flake8 SoftLayer tests