[Python-modules-commits] [python-softlayer] 01/04: Imported Upstream version 5.2.14

Scott Kitterman kitterman at moszumanska.debian.org
Sun Oct 8 00:27:17 UTC 2017


This is an automated email from the git hooks/post-receive script.

kitterman pushed a commit to branch debian/master
in repository python-softlayer.

commit 4a0845cadc442b6169fa54c6e05d8d3e0af67c30
Author: Scott Kitterman <scott at kitterman.com>
Date:   Sat Oct 7 20:17:22 2017 -0400

    Imported Upstream version 5.2.14
---
 .gitignore                                         |    2 +-
 .travis.yml                                        |    2 +-
 CHANGELOG.md                                       |   57 +
 README.rst                                         |    7 +-
 RELEASE.md                                         |    2 +-
 SoftLayer/CLI/block/access/password.py             |   27 +
 SoftLayer/CLI/block/count.py                       |   42 +
 SoftLayer/CLI/block/detail.py                      |   10 +-
 SoftLayer/CLI/block/duplicate.py                   |   15 +-
 SoftLayer/CLI/block/list.py                        |    5 +-
 SoftLayer/CLI/block/lun.py                         |   36 +
 SoftLayer/CLI/block/order.py                       |   47 +-
 SoftLayer/CLI/block/snapshot/order.py              |    5 +-
 SoftLayer/CLI/file/count.py                        |   41 +
 SoftLayer/CLI/file/detail.py                       |   14 +-
 SoftLayer/CLI/file/duplicate.py                    |   15 +-
 SoftLayer/CLI/file/list.py                         |    5 +-
 SoftLayer/CLI/file/order.py                        |   47 +-
 SoftLayer/CLI/file/snapshot/order.py               |    5 +-
 SoftLayer/CLI/hardware/detail.py                   |    5 +-
 SoftLayer/CLI/routes.py                            |   22 +
 SoftLayer/CLI/securitygroup/__init__.py            |    1 +
 SoftLayer/CLI/securitygroup/create.py              |   32 +
 SoftLayer/CLI/securitygroup/delete.py              |   17 +
 SoftLayer/CLI/securitygroup/detail.py              |   73 +
 SoftLayer/CLI/securitygroup/edit.py                |   28 +
 SoftLayer/CLI/securitygroup/interface.py           |  150 +
 SoftLayer/CLI/securitygroup/list.py                |   36 +
 SoftLayer/CLI/securitygroup/rule.py                |  140 +
 SoftLayer/CLI/storage_utils.py                     |   10 +
 SoftLayer/CLI/subnet/detail.py                     |   14 +-
 SoftLayer/CLI/virt/create.py                       |   78 +-
 SoftLayer/CLI/virt/create_options.py               |  102 +-
 SoftLayer/CLI/virt/detail.py                       |   14 +
 SoftLayer/consts.py                                |    2 +-
 SoftLayer/fixtures/SoftLayer_Account.py            |    2 +-
 .../fixtures/SoftLayer_Network_SecurityGroup.py    |   46 +
 SoftLayer/fixtures/SoftLayer_Network_Storage.py    |  157 +-
 .../SoftLayer_Network_Storage_Allowed_Host.py      |    1 +
 SoftLayer/fixtures/SoftLayer_Network_Subnet.py     |   30 +-
 SoftLayer/fixtures/SoftLayer_Product_Package.py    |  526 ++-
 SoftLayer/fixtures/SoftLayer_Virtual_Guest.py      |  240 +-
 SoftLayer/managers/block.py                        |  165 +-
 SoftLayer/managers/file.py                         |  158 +-
 SoftLayer/managers/firewall.py                     |   22 +-
 SoftLayer/managers/network.py                      |  212 ++
 SoftLayer/managers/storage_utils.py                |  616 +++-
 SoftLayer/managers/vs.py                           |  229 +-
 docs/conf.py                                       |    4 +-
 docs/dev/cli.rst                                   |   17 +-
 setup.py                                           |    2 +-
 tests/CLI/helper_tests.py                          |    9 +-
 tests/CLI/modules/block_tests.py                   |  193 +-
 tests/CLI/modules/config_tests.py                  |    3 +
 tests/CLI/modules/file_tests.py                    |  165 +-
 tests/CLI/modules/securitygroup_tests.py           |  233 ++
 tests/CLI/modules/server_tests.py                  |    6 +-
 tests/CLI/modules/sshkey_tests.py                  |    3 +
 tests/CLI/modules/subnet_tests.py                  |   37 +
 tests/CLI/modules/vs_tests.py                      |  167 +-
 tests/managers/block_tests.py                      | 1055 ++----
 tests/managers/file_tests.py                       | 1049 ++----
 tests/managers/firewall_tests.py                   |   34 +
 tests/managers/network_tests.py                    |  148 +
 tests/managers/storage_utils_tests.py              | 3844 ++++++++++++++++----
 tests/managers/vs_tests.py                         |   96 +-
 tox.ini                                            |    9 +-
 67 files changed, 7677 insertions(+), 2909 deletions(-)

diff --git a/.gitignore b/.gitignore
index 438f1d7..ebf9932 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,4 @@ build/*
 dist/*
 *.egg-info
 .cache
-
+.idea
diff --git a/.travis.yml b/.travis.yml
index fdb7114..c75b2b6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,7 +12,7 @@ matrix:
       env: TOX_ENV=py35
     - python: "3.6"
       env: TOX_ENV=py36
-    - python: "pypy"
+    - python: "pypy2.7-5.8.0"
       env: TOX_ENV=pypy
     - python: "2.7"
       env: TOX_ENV=analysis
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3433e6..910cc1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,62 @@
 # Change Log
 
+## [5.2.14] - 2017-09-13
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.13...master
+ - Improved slcli vs create-options output
+ - Updated slcli vs create to support new virtual server public and dedicated host offerings
+
+## [5.2.13] - 2017-09-05
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.12...v5.2.13
+ - Support for hourly billing of storage
+ - Added exception handling for Managers.VSManager.wait_for_ready()
+ - Added windows support for unit testing
+ - Updated pypy version
+ 
+## [5.2.12] - 2017-08-09
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.11...v5.2.12
+ - Support for storage_as_a_service block and file storage
+ 
+#### Added to CLI
+ - block volume-count
+ - file volume-count
+ - securitygroups
+   - create            Create a security group.
+   - delete            Deletes the given security group
+   - detail            Get details about a security group.
+   - edit              Edit details of a security group.
+   - interface-add     Attach an interface to a security group.
+   - interface-list    List interfaces associated with security...
+   - interface-remove  Detach an interface from a security group.
+   - list              List security groups.
+   - rule-add          Add a security group rule to a security...
+   - rule-edit         Edit a security group rule in a security...
+   - rule-list         List security group rules.
+   - rule-remove 
+
+## [5.2.11] - 2017-08-04
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.10...v5.2.11
+ - Sync VLAN and subnet detail CLI output
+
+## [5.2.10] - 2017-07-27
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.9...v5.2.10
+ - Avoid blindly passing memory result to formatter
+
+## [5.2.9] - 2017-07-27
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.8...v5.2.9
+ - Add support for dedicated host instances to virtual server upgrades
+#### Added to CLI
+* block volume-set-lun-id
+
+## [5.2.8] - 2017-07-19
+ - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.7...v5.2.8
+ 
+ * Resolved https://github.com/softlayer/softlayer-python/issues/835
+ * Resolved https://github.com/softlayer/softlayer-python/issues/826
+ * Fix dedicated/private VSI price retrieval for upgrades
+ 
+#### Added to CLI
+* block access-password
+
 ## [5.2.7] - 2017-06-22
  - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.6...v5.2.7
 
diff --git a/README.rst b/README.rst
index dba9d7f..b2c7617 100644
--- a/README.rst
+++ b/README.rst
@@ -14,11 +14,12 @@ SoftLayer API Python Client
 
 
 This library provides a simple Python client to interact with `SoftLayer's
-XML-RPC API <http://developer.softlayer.com/reference/softlayerapi>`_.
+XML-RPC API <http://developer.softlayer.com/reference/softlayerapi>`_. 
 
 A command-line interface is also included and can be used to manage various
 SoftLayer products and services.
 
+Development on this library is done as a best-effort delivery, and some features of the SoftLayer API may not be available through the client.
 
 Documentation
 -------------
@@ -49,7 +50,7 @@ Or you can install from source. Download source and run:
 
 
 The most up-to-date version of this library can be found on the SoftLayer
-GitHub public repositories at http://github.com/softlayer. Please post to Stack Overflow at https://stackoverflow.com/ or open a support ticket in the customer portal if you have any questions regarding use of this library. If you use Stack Overflow please tag your posts with “SoftLayer” so our team can easily find your post. 
+GitHub public repositories at http://github.com/softlayer. For questions regarding the use of this library please post to Stack Overflow at https://stackoverflow.com/ and  your posts with “SoftLayer” so our team can easily find your post. To report a bug with this library please create an Issue on github.
 
 InsecurePlatformWarning Notice
 ------------------------------
@@ -57,7 +58,7 @@ This library relies on the `requests <http://docs.python-requests.org/>`_ librar
 
 System Requirements
 -------------------
-* Python 2.7, 3.3, 3.4 or 3.5.
+* Python 2.7, 3.3, 3.4, 3.5 or 3.6.
 * A valid SoftLayer API username and key.
 * A connection to SoftLayer's private network is required to use
   our private network API endpoints.
diff --git a/RELEASE.md b/RELEASE.md
index d0a7712..75eea45 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -3,7 +3,7 @@
 * Update version constants (find them by running `git grep [VERSION_NUMBER]`)
 * Create changelog entry (edit CHANGELOG.md with a one-liner for each closed issue going in the release)
 * Commit and push changes to master with the message: "Version Bump to v[VERSION_NUMBER]"
-* Push tag and PyPi `fab release:[VERSION_NUMBER]`. Before you do this, make sure you have fabric installed (`pip install fabric`) and also make sure that you have pip set up with your PyPi user credentials. The easiest way to do that is to create a file at `~/.pypirc` with the following contents:
+* Push tag and PyPi `fab release:[VERSION_NUMBER]`. Before you do this, make sure you have the organization repository set up as upstream remote & fabric installed (`pip install fabric`), also make sure that you have pip set up with your PyPi user credentials. The easiest way to do that is to create a file at `~/.pypirc` with the following contents:
 
  ```
 [server-login]
diff --git a/SoftLayer/CLI/block/access/password.py b/SoftLayer/CLI/block/access/password.py
new file mode 100644
index 0000000..1046f25
--- /dev/null
+++ b/SoftLayer/CLI/block/access/password.py
@@ -0,0 +1,27 @@
+"""Modifies a password for a volume's access"""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+ at click.command()
+ at click.argument('access_id')
+ at click.option('--password', '-p', multiple=False,
+              help='Password you want to set, this command will fail if the password is not strong')
+ at environment.pass_env
+def cli(env, access_id, password):
+    """Changes a password for a volume's access.
+
+    access id is the allowed_host_id from slcli block access-list
+    """
+
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    result = block_manager.set_credential_password(access_id=access_id, password=password)
+
+    if result:
+        click.echo('Password updated for %s' % access_id)
+    else:
+        click.echo('FAILED updating password for %s' % access_id)
diff --git a/SoftLayer/CLI/block/count.py b/SoftLayer/CLI/block/count.py
new file mode 100644
index 0000000..dc4fb89
--- /dev/null
+++ b/SoftLayer/CLI/block/count.py
@@ -0,0 +1,42 @@
+"""List number of block storage volumes per datacenter."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+DEFAULT_COLUMNS = [
+    'Datacenter',
+    'Count'
+]
+
+
+ at click.command()
+ at click.option('--datacenter', '-d', help='Datacenter shortname')
+ at click.option('--sortby', help='Column to sort by', default='Datacenter')
+ at environment.pass_env
+def cli(env, sortby, datacenter):
+    """List number of block storage volumes per datacenter."""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    mask = "mask[serviceResource[datacenter[name]],"\
+           "replicationPartners[serviceResource[datacenter[name]]]]"
+    block_volumes = block_manager.list_block_volumes(datacenter=datacenter,
+                                                     mask=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_name = service_resource['datacenter']['name']
+            if datacenter_name not in datacenters.keys():
+                datacenters[datacenter_name] = 1
+            else:
+                datacenters[datacenter_name] += 1
+
+    table = formatting.KeyValueTable(DEFAULT_COLUMNS)
+    table.sortby = sortby
+    for datacenter_name in datacenters:
+        table.add_row([datacenter_name, datacenters[datacenter_name]])
+    env.fout(table)
diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py
index b8ba8be..70a41c0 100644
--- a/SoftLayer/CLI/block/detail.py
+++ b/SoftLayer/CLI/block/detail.py
@@ -86,16 +86,18 @@ def cli(env, volume_id):
                                                 replicant['id']])
             replicant_table.add_row([
                 'Volume Name',
-                replicant['username']])
+                utils.lookup(replicant, 'username')])
             replicant_table.add_row([
                 'Target IP',
-                replicant['serviceResourceBackendIpAddress']])
+                utils.lookup(replicant, 'serviceResourceBackendIpAddress')])
             replicant_table.add_row([
                 'Data Center',
-                replicant['serviceResource']['datacenter']['name']])
+                utils.lookup(replicant,
+                             'serviceResource', 'datacenter', 'name')])
             replicant_table.add_row([
                 'Schedule',
-                replicant['replicationSchedule']['type']['keyname']])
+                utils.lookup(replicant,
+                             'replicationSchedule', 'type', 'keyname')])
             replicant_list.append(replicant_table)
         table.add_row(['Replicant Volumes', replicant_list])
 
diff --git a/SoftLayer/CLI/block/duplicate.py b/SoftLayer/CLI/block/duplicate.py
index 98ef1b7..0ecf591 100644
--- a/SoftLayer/CLI/block/duplicate.py
+++ b/SoftLayer/CLI/block/duplicate.py
@@ -49,15 +49,23 @@ CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
               type=int,
               help='The size of snapshot space to order for the duplicate. '
                    '***If no snapshot space size is specified, the snapshot '
-                   'space size of the origin volume will be used.***\n'
+                   'space size of the origin block volume will be used.***\n'
                    'Input "0" for this parameter to order a duplicate volume '
                    'with no snapshot space.')
+ at click.option('--billing',
+              type=click.Choice(['hourly', 'monthly']),
+              default='monthly',
+              help="Optional parameter for Billing rate (default to monthly)")
 @environment.pass_env
 def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size,
-        duplicate_iops, duplicate_tier, duplicate_snapshot_size):
+        duplicate_iops, duplicate_tier, duplicate_snapshot_size, billing):
     """Order a duplicate block storage volume."""
     block_manager = SoftLayer.BlockStorageManager(env.client)
 
+    hourly_billing_flag = False
+    if billing.lower() == "hourly":
+        hourly_billing_flag = True
+
     if duplicate_tier is not None:
         duplicate_tier = float(duplicate_tier)
 
@@ -68,7 +76,8 @@ def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size,
             duplicate_size=duplicate_size,
             duplicate_iops=duplicate_iops,
             duplicate_tier_level=duplicate_tier,
-            duplicate_snapshot_size=duplicate_snapshot_size
+            duplicate_snapshot_size=duplicate_snapshot_size,
+            hourly_billing_flag=hourly_billing_flag
         )
     except ValueError as ex:
         raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/block/list.py b/SoftLayer/CLI/block/list.py
index 41e09ca..4cc9afd 100644
--- a/SoftLayer/CLI/block/list.py
+++ b/SoftLayer/CLI/block/list.py
@@ -28,6 +28,8 @@ COLUMNS = [
     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 @@ DEFAULT_COLUMNS = [
     'bytes_used',
     'ip_addr',
     'lunId',
-    'active_transactions'
+    'active_transactions',
+    'rep_partner_count'
 ]
 
 
diff --git a/SoftLayer/CLI/block/lun.py b/SoftLayer/CLI/block/lun.py
new file mode 100644
index 0000000..ee33a23
--- /dev/null
+++ b/SoftLayer/CLI/block/lun.py
@@ -0,0 +1,36 @@
+"""Set the LUN ID on an iSCSI volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+ at click.command()
+ at click.argument('volume-id')
+ at click.argument('lun-id')
+ at environment.pass_env
+def cli(env, volume_id, lun_id):
+    """Set the LUN ID on an existing block storage volume.
+
+    The LUN ID only takes effect during the Host Authorization process. It is
+    recommended (but not necessary) to de-authorize all hosts before using this
+    method. See `block access-revoke`.
+
+    VOLUME_ID - the volume ID on which to set the LUN ID.
+
+    LUN_ID - recommended range is an integer between 0 and 255. Advanced users
+    can use an integer between 0 and 4095.
+    """
+
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    res = block_storage_manager.create_or_update_lun_id(volume_id, lun_id)
+
+    if 'value' in res and lun_id == res['value']:
+        click.echo(
+            'Block volume with id %s is reporting LUN ID %s' % (res['volumeId'], res['value']))
+    else:
+        click.echo(
+            'Failed to confirm the new LUN ID on volume %s' % (volume_id))
diff --git a/SoftLayer/CLI/block/order.py b/SoftLayer/CLI/block/order.py
index a5af2a6..abd7dd9 100644
--- a/SoftLayer/CLI/block/order.py
+++ b/SoftLayer/CLI/block/order.py
@@ -48,40 +48,61 @@ CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
               help='Optional parameter for ordering snapshot '
               'space along with endurance block storage; specifies '
               'the size (in GB) of snapshot space to order')
+ at click.option('--service-offering',
+              help='The service offering package to use for placing '
+              'the order [optional, default is \'storage_as_a_service\']',
+              default='storage_as_a_service',
+              type=click.Choice([
+                  'storage_as_a_service',
+                  'enterprise',
+                  'performance']))
+ at click.option('--billing',
+              type=click.Choice(['hourly', 'monthly']),
+              default='monthly',
+              help="Optional parameter for Billing rate (default to monthly)")
 @environment.pass_env
 def cli(env, storage_type, size, iops, tier, os_type,
-        location, snapshot_size):
+        location, snapshot_size, service_offering, billing):
     """Order a block storage volume."""
     block_manager = SoftLayer.BlockStorageManager(env.client)
     storage_type = storage_type.lower()
 
+    hourly_billing_flag = False
+    if billing.lower() == "hourly":
+        hourly_billing_flag = True
+
+    if hourly_billing_flag and service_offering != 'storage_as_a_service':
+        raise exceptions.CLIAbort(
+            'Hourly billing is only available for the storage_as_a_service '
+            'service offering'
+        )
+
     if storage_type == 'performance':
         if iops is None:
             raise exceptions.CLIAbort(
                 'Option --iops required with Performance')
 
-        if iops < 100 or iops > 6000:
-            raise exceptions.CLIAbort(
-                'Option --iops must be between 100 and 6000, inclusive')
-
         if iops % 100 != 0:
             raise exceptions.CLIAbort(
                 'Option --iops must be a multiple of 100'
             )
 
-        if snapshot_size is not None:
+        if service_offering == 'performance' and snapshot_size is not None:
             raise exceptions.CLIAbort(
-                'Option --snapshot-size not allowed for performance volumes.'
-                'Snapshots are only available for endurance storage.'
+                '--snapshot-size is not available for performance volumes '
+                'ordered with the \'performance\' service offering option'
             )
 
         try:
             order = block_manager.order_block_volume(
-                storage_type='performance_storage_iscsi',
+                storage_type=storage_type,
                 location=location,
                 size=int(size),
                 iops=iops,
-                os_type=os_type
+                os_type=os_type,
+                snapshot_size=snapshot_size,
+                service_offering=service_offering,
+                hourly_billing_flag=hourly_billing_flag
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
@@ -95,12 +116,14 @@ def cli(env, storage_type, size, iops, tier, os_type,
 
         try:
             order = block_manager.order_block_volume(
-                storage_type='storage_service_enterprise',
+                storage_type=storage_type,
                 location=location,
                 size=int(size),
                 tier_level=float(tier),
                 os_type=os_type,
-                snapshot_size=snapshot_size
+                snapshot_size=snapshot_size,
+                service_offering=service_offering,
+                hourly_billing_flag=hourly_billing_flag
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/block/snapshot/order.py b/SoftLayer/CLI/block/snapshot/order.py
index 119c4d8..c5d798e 100644
--- a/SoftLayer/CLI/block/snapshot/order.py
+++ b/SoftLayer/CLI/block/snapshot/order.py
@@ -15,8 +15,9 @@ from SoftLayer.CLI import exceptions
               required=True)
 @click.option('--tier',
               help='Endurance Storage Tier (IOPS per GB) of the block'
-              ' volume for which space is ordered [optional]',
-              type=click.Choice(['0.25', '2', '4']))
+              ' volume for which space is ordered [optional, and only'
+              ' valid for endurance storage volumes]',
+              type=click.Choice(['0.25', '2', '4', '10']))
 @click.option('--upgrade',
               type=bool,
               help='Flag to indicate that the order is an upgrade',
diff --git a/SoftLayer/CLI/file/count.py b/SoftLayer/CLI/file/count.py
new file mode 100644
index 0000000..addb143
--- /dev/null
+++ b/SoftLayer/CLI/file/count.py
@@ -0,0 +1,41 @@
+"""List number of file storage volumes per datacenter."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+DEFAULT_COLUMNS = [
+    'Datacenter',
+    'Count'
+]
+
+
+ at click.command()
+ at click.option('--datacenter', '-d', help='Datacenter shortname')
+ at click.option('--sortby', help='Column to sort by', default='Datacenter')
+ at environment.pass_env
+def cli(env, sortby, datacenter):
+    """List number of file storage volumes per datacenter."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    mask = "mask[serviceResource[datacenter[name]],"\
+           "replicationPartners[serviceResource[datacenter[name]]]]"
+    file_volumes = file_manager.list_file_volumes(datacenter=datacenter,
+                                                  mask=mask)
+
+    datacenters = dict()
+    for volume in file_volumes:
+        service_resource = volume['serviceResource']
+        if 'datacenter' in service_resource:
+            datacenter_name = service_resource['datacenter']['name']
+            if datacenter_name not in datacenters.keys():
+                datacenters[datacenter_name] = 1
+            else:
+                datacenters[datacenter_name] += 1
+
+    table = formatting.KeyValueTable(DEFAULT_COLUMNS)
+    table.sortby = sortby
+    for datacenter_name in datacenters:
+        table.add_row([datacenter_name, datacenters[datacenter_name]])
+    env.fout(table)
diff --git a/SoftLayer/CLI/file/detail.py b/SoftLayer/CLI/file/detail.py
index 3c9af3f..96437dc 100644
--- a/SoftLayer/CLI/file/detail.py
+++ b/SoftLayer/CLI/file/detail.py
@@ -16,8 +16,6 @@ def cli(env, volume_id):
     file_manager = SoftLayer.FileStorageManager(env.client)
     file_volume = file_manager.get_file_volume_details(volume_id)
     file_volume = utils.NestedDict(file_volume)
-    used_space = int(file_volume['bytesUsed'])\
-        if file_volume['bytesUsed'] else 0
 
     table = formatting.KeyValueTable(['Name', 'Value'])
     table.align['Name'] = 'r'
@@ -29,6 +27,8 @@ def cli(env, volume_id):
     table.add_row(['Type', storage_type])
     table.add_row(['Capacity (GB)', "%iGB" % file_volume['capacityGb']])
 
+    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):
@@ -102,16 +102,18 @@ def cli(env, volume_id):
                                                 replicant['id']])
             replicant_table.add_row([
                 'Volume Name',
-                replicant['username']])
+                utils.lookup(replicant, 'username')])
             replicant_table.add_row([
                 'Target IP',
-                replicant['serviceResourceBackendIpAddress']])
+                utils.lookup(replicant, 'serviceResourceBackendIpAddress')])
             replicant_table.add_row([
                 'Data Center',
-                replicant['serviceResource']['datacenter']['name']])
+                utils.lookup(replicant,
+                             'serviceResource', 'datacenter', 'name')])
             replicant_table.add_row([
                 'Schedule',
-                replicant['replicationSchedule']['type']['keyname']])
+                utils.lookup(replicant,
+                             'replicationSchedule', 'type', 'keyname')])
             replicant_list.append(replicant_table)
         table.add_row(['Replicant Volumes', replicant_list])
 
diff --git a/SoftLayer/CLI/file/duplicate.py b/SoftLayer/CLI/file/duplicate.py
index e021697..a3b4c80 100644
--- a/SoftLayer/CLI/file/duplicate.py
+++ b/SoftLayer/CLI/file/duplicate.py
@@ -45,15 +45,23 @@ CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
               type=int,
               help='The size of snapshot space to order for the duplicate. '
                    '***If no snapshot space size is specified, the snapshot '
-                   'space size of the origin volume will be used.***\n'
+                   'space size of the origin file volume will be used.***\n'
                    'Input "0" for this parameter to order a duplicate volume '
                    'with no snapshot space.')
+ at click.option('--billing',
+              type=click.Choice(['hourly', 'monthly']),
+              default='monthly',
+              help="Optional parameter for Billing rate (default to monthly)")
 @environment.pass_env
 def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size,
-        duplicate_iops, duplicate_tier, duplicate_snapshot_size):
+        duplicate_iops, duplicate_tier, duplicate_snapshot_size, billing):
     """Order a duplicate file storage volume."""
     file_manager = SoftLayer.FileStorageManager(env.client)
 
+    hourly_billing_flag = False
+    if billing.lower() == "hourly":
+        hourly_billing_flag = True
+
     if duplicate_tier is not None:
         duplicate_tier = float(duplicate_tier)
 
@@ -64,7 +72,8 @@ def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size,
             duplicate_size=duplicate_size,
             duplicate_iops=duplicate_iops,
             duplicate_tier_level=duplicate_tier,
-            duplicate_snapshot_size=duplicate_snapshot_size
+            duplicate_snapshot_size=duplicate_snapshot_size,
+            hourly_billing_flag=hourly_billing_flag
         )
     except ValueError as ex:
         raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/file/list.py b/SoftLayer/CLI/file/list.py
index 4039953..86028f4 100644
--- a/SoftLayer/CLI/file/list.py
+++ b/SoftLayer/CLI/file/list.py
@@ -29,6 +29,8 @@ COLUMNS = [
                          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 @@ DEFAULT_COLUMNS = [
     'bytes_used',
     'ip_addr',
     'active_transactions',
-    'mount_addr'
+    'mount_addr',
+    'rep_partner_count'
 ]
 
 
diff --git a/SoftLayer/CLI/file/order.py b/SoftLayer/CLI/file/order.py
index d13b952..ae90133 100644
--- a/SoftLayer/CLI/file/order.py
+++ b/SoftLayer/CLI/file/order.py
@@ -36,39 +36,60 @@ CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
               help='Optional parameter for ordering snapshot '
               'space along with endurance file storage; specifies '
               'the size (in GB) of snapshot space to order')
+ at click.option('--service-offering',
+              help='The service offering package to use for placing '
+              'the order [optional, default is \'storage_as_a_service\']',
+              default='storage_as_a_service',
+              type=click.Choice([
+                  'storage_as_a_service',
+                  'enterprise',
+                  'performance']))
+ at click.option('--billing',
+              type=click.Choice(['hourly', 'monthly']),
+              default='monthly',
+              help="Optional parameter for Billing rate (default to monthly)")
 @environment.pass_env
 def cli(env, storage_type, size, iops, tier,
-        location, snapshot_size):
+        location, snapshot_size, service_offering, billing):
     """Order a file storage volume."""
     file_manager = SoftLayer.FileStorageManager(env.client)
     storage_type = storage_type.lower()
 
+    hourly_billing_flag = False
+    if billing.lower() == "hourly":
+        hourly_billing_flag = True
+
+    if hourly_billing_flag and service_offering != 'storage_as_a_service':
+        raise exceptions.CLIAbort(
+            'Hourly billing is only available for the storage_as_a_service '
+            'service offering'
+        )
+
     if storage_type == 'performance':
         if iops is None:
             raise exceptions.CLIAbort(
                 'Option --iops required with Performance')
 
-        if iops < 100 or iops > 6000:
-            raise exceptions.CLIAbort(
-                'Option --iops must be between 100 and 6000, inclusive')
-
         if iops % 100 != 0:
             raise exceptions.CLIAbort(
                 'Option --iops must be a multiple of 100'
             )
 
-        if snapshot_size is not None:
+        if service_offering == 'performance' and snapshot_size is not None:
             raise exceptions.CLIAbort(
-                'Option --snapshot-size not allowed for performance volumes.'
-                ' Snapshots are only available for endurance storage.'
+                '--snapshot-size is not available for performance volumes '
+                'ordered with the \'performance\' service offering option'
             )
 
         try:
             order = file_manager.order_file_volume(
-                storage_type='performance_storage_nfs',
+                storage_type=storage_type,
                 location=location,
                 size=size,
-                iops=iops
+                iops=iops,
+                snapshot_size=snapshot_size,
+                service_offering=service_offering,
+                hourly_billing_flag=hourly_billing_flag
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
@@ -82,11 +103,13 @@ def cli(env, storage_type, size, iops, tier,
 
         try:
             order = file_manager.order_file_volume(
-                storage_type='storage_service_enterprise',
+                storage_type=storage_type,
                 location=location,
                 size=size,
                 tier_level=float(tier),
-                snapshot_size=snapshot_size
+                snapshot_size=snapshot_size,
+                service_offering=service_offering,
+                hourly_billing_flag=hourly_billing_flag
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/file/snapshot/order.py b/SoftLayer/CLI/file/snapshot/order.py
index fbfff51..9043f8b 100644
--- a/SoftLayer/CLI/file/snapshot/order.py
+++ b/SoftLayer/CLI/file/snapshot/order.py
@@ -15,8 +15,9 @@ from SoftLayer.CLI import exceptions
               required=True)
 @click.option('--tier',
               help='Endurance Storage Tier (IOPS per GB) of the file'
-              ' volume for which space is ordered [optional]',
-              type=click.Choice(['0.25', '2', '4']))
+              ' volume for which space is ordered [optional, and only'
+              ' valid for endurance storage volumes]',
+              type=click.Choice(['0.25', '2', '4', '10']))
 @click.option('--upgrade',
               type=bool,
               help='Flag to indicate that the order is an upgrade',
diff --git a/SoftLayer/CLI/hardware/detail.py b/SoftLayer/CLI/hardware/detail.py
index 7bd4454..ebfbbca 100644
--- a/SoftLayer/CLI/hardware/detail.py
+++ b/SoftLayer/CLI/hardware/detail.py
@@ -43,7 +43,10 @@ def cli(env, identifier, passwords, price):
     table.add_row(['datacenter',
                    result['datacenter']['name'] or formatting.blank()])
     table.add_row(['cores', result['processorPhysicalCoreAmount']])
-    table.add_row(['memory', formatting.gb(result['memoryCapacity'])])
+    memory = (formatting.gb(result['memoryCapacity'])
+              if result.get('memoryCapacity')
+              else formatting.blank())
+    table.add_row(['memory', memory])
     table.add_row(['public_ip',
                    result['primaryIpAddress'] or formatting.blank()])
     table.add_row(['private_ip',
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index ae33226..eae0c76 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -60,6 +60,7 @@ ALL_ROUTES = [
     ('block:access-authorize', 'SoftLayer.CLI.block.access.authorize:cli'),
     ('block:access-list', 'SoftLayer.CLI.block.access.list:cli'),
     ('block:access-revoke', 'SoftLayer.CLI.block.access.revoke:cli'),
+    ('block:access-password', 'SoftLayer.CLI.block.access.password:cli'),
     ('block:replica-failback', 'SoftLayer.CLI.block.replication.failback:cli'),
     ('block:replica-failover', 'SoftLayer.CLI.block.replication.failover:cli'),
     ('block:replica-order', 'SoftLayer.CLI.block.replication.order:cli'),
@@ -75,10 +76,12 @@ ALL_ROUTES = [
     ('block:snapshot-order', 'SoftLayer.CLI.block.snapshot.order:cli'),
     ('block:snapshot-restore', 'SoftLayer.CLI.block.snapshot.restore:cli'),
     ('block:volume-cancel', 'SoftLayer.CLI.block.cancel:cli'),
+    ('block:volume-count', 'SoftLayer.CLI.block.count:cli'),
     ('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'),
     ('block:volume-duplicate', 'SoftLayer.CLI.block.duplicate:cli'),
     ('block:volume-list', 'SoftLayer.CLI.block.list:cli'),
     ('block:volume-order', 'SoftLayer.CLI.block.order:cli'),
+    ('block:volume-set-lun-id', 'SoftLayer.CLI.block.lun:cli'),
 
     ('file', 'SoftLayer.CLI.file'),
     ('file:access-authorize', 'SoftLayer.CLI.file.access.authorize:cli'),
@@ -98,6 +101,7 @@ ALL_ROUTES = [
     ('file:snapshot-order', 'SoftLayer.CLI.file.snapshot.order:cli'),
     ('file:snapshot-restore', 'SoftLayer.CLI.file.snapshot.restore:cli'),
     ('file:volume-cancel', 'SoftLayer.CLI.file.cancel:cli'),
+    ('file:volume-count', 'SoftLayer.CLI.file.count:cli'),
     ('file:volume-detail', 'SoftLayer.CLI.file.detail:cli'),
     ('file:volume-duplicate', 'SoftLayer.CLI.file.duplicate:cli'),
     ('file:volume-list', 'SoftLayer.CLI.file.list:cli'),
@@ -207,6 +211,23 @@ ALL_ROUTES = [
     ('hardware:credentials', 'SoftLayer.CLI.hardware.credentials:cli'),
     ('hardware:update-firmware', 'SoftLayer.CLI.hardware.update_firmware:cli'),
 
+    ('securitygroup', 'SoftLayer.CLI.securitygroup'),
+    ('securitygroup:list', 'SoftLayer.CLI.securitygroup.list:cli'),
+    ('securitygroup:detail', 'SoftLayer.CLI.securitygroup.detail:cli'),
+    ('securitygroup:create', 'SoftLayer.CLI.securitygroup.create:cli'),
+    ('securitygroup:edit', 'SoftLayer.CLI.securitygroup.edit:cli'),
+    ('securitygroup:delete', 'SoftLayer.CLI.securitygroup.delete:cli'),
+    ('securitygroup:rule-list', 'SoftLayer.CLI.securitygroup.rule:rule_list'),
+    ('securitygroup:rule-add', 'SoftLayer.CLI.securitygroup.rule:add'),
+    ('securitygroup:rule-edit', 'SoftLayer.CLI.securitygroup.rule:edit'),
+    ('securitygroup:rule-remove', 'SoftLayer.CLI.securitygroup.rule:remove'),
+    ('securitygroup:interface-list',
+     'SoftLayer.CLI.securitygroup.interface:interface_list'),
+    ('securitygroup:interface-add',
+     'SoftLayer.CLI.securitygroup.interface:add'),
+    ('securitygroup:interface-remove',
+     'SoftLayer.CLI.securitygroup.interface:remove'),
+
     ('sshkey', 'SoftLayer.CLI.sshkey'),
     ('sshkey:add', 'SoftLayer.CLI.sshkey.add:cli'),
     ('sshkey:remove', 'SoftLayer.CLI.sshkey.remove:cli'),
@@ -254,6 +275,7 @@ ALL_ALIASES = {
     'lb': 'loadbal',
     'meta': 'metadata',
     'my': 'metadata',
+    'sg': 'securitygroup',
     'server': 'hardware',
     'vm': 'virtual',
     'vs': 'virtual',
diff --git a/SoftLayer/CLI/securitygroup/__init__.py b/SoftLayer/CLI/securitygroup/__init__.py
new file mode 100644
index 0000000..936e276
--- /dev/null
+++ b/SoftLayer/CLI/securitygroup/__init__.py
@@ -0,0 +1 @@
+"""Network security groups."""
diff --git a/SoftLayer/CLI/securitygroup/create.py b/SoftLayer/CLI/securitygroup/create.py
new file mode 100644
index 0000000..5fc5994
--- /dev/null
+++ b/SoftLayer/CLI/securitygroup/create.py
@@ -0,0 +1,32 @@
+"""Create security groups."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+
+ at click.command()
+ at click.option('--name', '-n',
+              help="The name of the security group")
+ at click.option('--description', '-d',
+              help="The description of the security group")
+ at environment.pass_env
+def cli(env, name, description):
+    """Create a security group."""
+    mgr = SoftLayer.NetworkManager(env.client)
+
+    result = mgr.create_securitygroup(name, description)
+    table = formatting.KeyValueTable(['name', 'value'])
+    table.align['name'] = 'r'
+    table.align['value'] = 'l'
+    table.add_row(['id', result['id']])
+    table.add_row(['name',
+                   result.get('name') or formatting.blank()])
+    table.add_row(['description',
+                   result.get('description') or formatting.blank()])
+    table.add_row(['created', result['createDate']])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/securitygroup/delete.py b/SoftLayer/CLI/securitygroup/delete.py
new file mode 100644
index 0000000..4cbca3e
--- /dev/null
+++ b/SoftLayer/CLI/securitygroup/delete.py
@@ -0,0 +1,17 @@
+"""Delete a security group."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+ at click.command()
+ at click.argument('securitygroup_id')
+ at environment.pass_env
+def cli(env, securitygroup_id):
+    """Deletes the given security group"""
+    mgr = SoftLayer.NetworkManager(env.client)
+    if not mgr.delete_securitygroup(securitygroup_id):
+        raise exceptions.CLIAbort("Failed to delete security group")
diff --git a/SoftLayer/CLI/securitygroup/detail.py b/SoftLayer/CLI/securitygroup/detail.py
new file mode 100644
index 0000000..4a3381f
--- /dev/null
+++ b/SoftLayer/CLI/securitygroup/detail.py
@@ -0,0 +1,73 @@
+"""Get details about a security group."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+
+ at click.command()
+ at click.argument('identifier')
+ at environment.pass_env
+def cli(env, identifier):
+    """Get details about a security group."""
+
+    mgr = SoftLayer.NetworkManager(env.client)
+
+    secgroup = mgr.get_securitygroup(identifier)
+
+    table = formatting.KeyValueTable(['name', 'value'])
+    table.align['name'] = 'r'
+    table.align['value'] = 'l'
+
+    table.add_row(['id', secgroup['id']])
+    table.add_row(['name', secgroup.get('name') or formatting.blank()])
+    table.add_row(['description',
+                   secgroup.get('description') or formatting.blank()])
+
+    rule_table = formatting.Table(['id', 'remoteIp', 'remoteGroupId',
+                                   'direction', 'ethertype', 'portRangeMin',
+                                   'portRangeMax', 'protocol'])
+    for rule in secgroup.get('rules', []):
+        rg_id = rule.get('remoteGroup', {}).get('id') or formatting.blank()
+        port_min = rule.get('portRangeMin')
+        port_max = rule.get('portRangeMax')
+        if port_min is None:
+            port_min = formatting.blank()
+        if port_max is None:
+            port_max = formatting.blank()
+        rule_table.add_row([rule['id'],
+                            rule.get('remoteIp') or formatting.blank(),
+                            rule.get('remoteGroupId', rg_id),
+                            rule['direction'],
+                            rule.get('ethertype') or formatting.blank(),
+                            port_min,
+                            port_max,
... 12778 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-softlayer.git



More information about the Python-modules-commits mailing list