[Python-modules-commits] [python-softlayer] 01/05: Import python-softlayer_5.2.0.orig.tar.gz

Scott Kitterman kitterman at moszumanska.debian.org
Fri Sep 9 19:12:21 UTC 2016


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

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

commit 08809a72b57bec66c5b7cd6b97af2baeb4d8e396
Author: Scott Kitterman <scott at kitterman.com>
Date:   Fri Sep 9 14:47:54 2016 -0400

    Import python-softlayer_5.2.0.orig.tar.gz
---
 CHANGELOG                                          |  18 +
 CONTRIBUTING.md                                    |   4 +-
 CONTRIBUTORS                                       |   1 +
 README.rst                                         |   2 +-
 SoftLayer/API.py                                   |  68 +-
 SoftLayer/CLI/block/access/__init__.py             |   1 +
 SoftLayer/CLI/block/access/authorize.py            |  45 ++
 SoftLayer/CLI/block/access/list.py                 |  38 +
 SoftLayer/CLI/block/access/revoke.py               |  41 ++
 SoftLayer/CLI/block/cancel.py                      |  13 +-
 SoftLayer/CLI/block/detail.py                      |  51 +-
 SoftLayer/CLI/block/list.py                        |  15 +-
 SoftLayer/CLI/block/order.py                       |  23 +-
 SoftLayer/CLI/block/replication/__init__.py        |   1 +
 SoftLayer/CLI/block/replication/failback.py        |  25 +
 SoftLayer/CLI/block/replication/failover.py        |  30 +
 SoftLayer/CLI/block/replication/order.py           |  65 ++
 SoftLayer/CLI/block/snapshot/__init__.py           |   1 +
 SoftLayer/CLI/block/snapshot/cancel.py             |  40 ++
 SoftLayer/CLI/block/snapshot/create.py             |  24 +
 .../{snapshot_delete.py => snapshot/delete.py}     |   7 +-
 SoftLayer/CLI/block/snapshot/disable.py            |  29 +
 SoftLayer/CLI/block/snapshot/enable.py             |  58 ++
 .../block/{snapshot_list.py => snapshot/list.py}   |  23 +-
 SoftLayer/CLI/block/snapshot/order.py              |  52 ++
 SoftLayer/CLI/block/snapshot/restore.py            |  22 +
 SoftLayer/CLI/call_api.py                          |  34 +-
 SoftLayer/CLI/environment.py                       |   2 +-
 SoftLayer/CLI/file/__init__.py                     |   1 +
 SoftLayer/CLI/file/access/__init__.py              |   1 +
 SoftLayer/CLI/file/access/authorize.py             |  49 ++
 SoftLayer/CLI/file/access/list.py                  |  38 +
 SoftLayer/CLI/file/access/revoke.py                |  46 ++
 SoftLayer/CLI/file/cancel.py                       |  39 ++
 SoftLayer/CLI/file/detail.py                       | 111 +++
 SoftLayer/CLI/{block => file}/list.py              |  31 +-
 SoftLayer/CLI/{block => file}/order.py             |  35 +-
 SoftLayer/CLI/file/replication/__init__.py         |   1 +
 SoftLayer/CLI/file/replication/failback.py         |  26 +
 SoftLayer/CLI/file/replication/failover.py         |  30 +
 SoftLayer/CLI/file/replication/order.py            |  53 ++
 SoftLayer/CLI/file/snapshot/__init__.py            |   1 +
 SoftLayer/CLI/file/snapshot/cancel.py              |  40 ++
 SoftLayer/CLI/file/snapshot/create.py              |  24 +
 .../snapshot_delete.py => file/snapshot/delete.py} |   9 +-
 SoftLayer/CLI/file/snapshot/disable.py             |  29 +
 SoftLayer/CLI/file/snapshot/enable.py              |  58 ++
 .../snapshot_list.py => file/snapshot/list.py}     |  31 +-
 SoftLayer/CLI/file/snapshot/order.py               |  52 ++
 SoftLayer/CLI/file/snapshot/restore.py             |  22 +
 SoftLayer/CLI/formatting.py                        |  17 +-
 SoftLayer/CLI/hardware/detail.py                   |  28 +-
 SoftLayer/CLI/report/__init__.py                   |   1 +
 SoftLayer/CLI/report/bandwidth.py                  | 231 +++++++
 SoftLayer/CLI/routes.py                            |  45 +-
 .../CLI/{block/access_list.py => storage_utils.py} |  35 +-
 SoftLayer/CLI/subnet/detail.py                     |   3 +
 SoftLayer/CLI/subnet/list.py                       |  13 +-
 SoftLayer/CLI/ticket/upload.py                     |  37 +
 SoftLayer/CLI/virt/create_options.py               |   2 +-
 SoftLayer/CLI/virt/detail.py                       |  18 +-
 SoftLayer/consts.py                                |   2 +-
 SoftLayer/fixtures/SoftLayer_Account.py            |  24 +-
 SoftLayer/fixtures/SoftLayer_Hardware_Server.py    |  12 +-
 .../fixtures/SoftLayer_Metric_Tracking_Object.py   |   1 +
 SoftLayer/fixtures/SoftLayer_Network_Storage.py    |  59 +-
 SoftLayer/fixtures/SoftLayer_Ticket.py             |  12 +
 SoftLayer/fixtures/SoftLayer_Virtual_Guest.py      |   9 +-
 ...er_Virtual_Guest_Block_Device_Template_Group.py |   6 +-
 SoftLayer/managers/__init__.py                     |   2 +
 SoftLayer/managers/block.py                        | 492 +++++++------
 SoftLayer/managers/cdn.py                          |   8 +-
 SoftLayer/managers/dns.py                          |   6 +-
 SoftLayer/managers/file.py                         | 473 +++++++++++++
 SoftLayer/managers/firewall.py                     |   8 +-
 SoftLayer/managers/hardware.py                     |  66 +-
 SoftLayer/managers/image.py                        |   7 +-
 SoftLayer/managers/iscsi.py                        |   9 +-
 SoftLayer/managers/load_balancer.py                |   7 +-
 SoftLayer/managers/messaging.py                    |  10 +-
 SoftLayer/managers/metadata.py                     |   7 +-
 SoftLayer/managers/network.py                      |  18 +-
 SoftLayer/managers/object_storage.py               |   8 +-
 SoftLayer/managers/ordering.py                     |   4 +-
 SoftLayer/managers/sshkey.py                       |   7 +-
 SoftLayer/managers/ssl.py                          |   6 +-
 SoftLayer/managers/storage_utils.py                | 454 ++++++++++++
 SoftLayer/managers/ticket.py                       |  47 +-
 SoftLayer/managers/vs.py                           |  95 +--
 SoftLayer/testing/__init__.py                      |  11 +-
 SoftLayer/testing/xmlrpc.py                        |   7 +-
 docs/conf.py                                       |   4 +-
 docs/{ => dev}/cla-corporate.md                    |   0
 docs/{ => dev}/cla-individual.md                   |   0
 docs/dev/index.rst                                 |  23 +-
 setup.py                                           |   2 +-
 tests/CLI/helper_tests.py                          |  14 +
 tests/CLI/modules/block_tests.py                   | 304 +++++++-
 tests/CLI/modules/call_api_tests.py                |  45 ++
 tests/CLI/modules/file_tests.py                    | 400 +++++++++++
 tests/CLI/modules/report_tests.py                  | 185 +++++
 tests/CLI/modules/server_tests.py                  |   5 +-
 tests/CLI/modules/ticket_tests.py                  |  37 +
 tests/CLI/modules/vs_tests.py                      |   4 +-
 tests/api_tests.py                                 |  44 +-
 tests/managers/block_tests.py                      | 612 +++++++++++++++-
 tests/managers/file_tests.py                       | 766 +++++++++++++++++++++
 tests/managers/network_tests.py                    |  14 +-
 tests/resources/attachment_upload                  |   1 +
 109 files changed, 5657 insertions(+), 570 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 60b8a22..07b9dd0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,21 @@
+5.2.0
+
+    * Significant additions to `slcli file` and `slcli block` commands. You can now authorize hosts, revoke access. You can also create, delete, restore, disable, enable snapshots. These features need to be battle-tested so report any issues that you see.
+
+    * Adds logic to `SoftLayer.create_client_from_env` that detects if a REST endpoint_url was given in order to use the REST transport automatically. This means that you can also configure REST endpoints for `slcli`. The default still uses XML-RPC endpoint, but from a small amount of testing shows that the REST transport is significantly faster.
+
+    * Adds `--network-space` to `slcli subnet list` in order to filter subnets based on network space. The two main options are PUBLIC and PRIVATE. For example, to list all public subnets, you can run: `slcli subnet list --network-space=PUBLIC`
+
+    * Fixes a UnicodeEncodeError when piping slcli output with unicode characters. This was mostly reported with `slcli image list` but could also happen with many other calls.
+
+    *  Adds a new, non-default column, "created_by" that shows who ordered the volume for `slcli file volume-list` and `slcli block volume-list`.
+
+    * Fixed a bug where os_version was not displaying correctly in `slcli virtual detail` or `slcli virtual detail`
+
+    * Adds a new `slcli report bandwidth` command that will print a report of all bandwidth pools and virtual/hardware servers that your user has access to.
+
+    * Adds an "IN" syntax to the `slcli call-api` command. For example, to find VSIs that are in either the dal05 or sng01 datacenter you can run this command: `slcli call-api Account getVirtualGuests -f 'virtualGuests.datacenter.name IN dal05,sng01'`
+
 5.1.0
 
     * Added block storage functionality. You can order, list, detail, cancel volumes. You can list and delete snapshots. You can also list ACLs for volumes.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7a82eb1..460e436 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,9 +3,9 @@
 We are happy to accept contributions to softlayer-python.  Please follow the
 guidelines below.  
 
-* Sign our contributor agreement (CLA) You can find the [CLA here](./docs/cla-individual.md).
+* Sign our contributor agreement (CLA) You can find the [CLA here](./docs/dev/cla-individual.md).
 
-* If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well.  You can find the [CCLA here](./docs/cla-corporate.md).
+* If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well.  You can find the [CCLA here](./docs/dev/cla-corporate.md).
     
 * Fork the repo, make your changes, and open a pull request.
 
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 8844223..6197a85 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -11,6 +11,7 @@ Jason Johnson <spligak at gmail.com>
 Kevin Landreth <klandreth at softlayer.com>
 Kevin McDonald <kmcdonald at softlayer.com>
 Łukasz Oleś <loles at mirantis.com>
+Michael Fork <mjfork at us.ibm.com>
 Nathan Beittenmiller <nbeittenmiller at softlayer.com>
 Neetu Jain <njain at softlayer.com>
 Paul Sroufe <psroufe at softlayer.com>
diff --git a/README.rst b/README.rst
index 31da9b6..8d46772 100644
--- a/README.rst
+++ b/README.rst
@@ -57,7 +57,7 @@ This library relies on the `requests <http://docs.python-requests.org/>`_ librar
 
 System Requirements
 -------------------
-* Python 2.7, 3.3 or higher.
+* Python 2.7, 3.3, 3.4 or 3.5.
 * 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/SoftLayer/API.py b/SoftLayer/API.py
index dc887d4..322309f 100644
--- a/SoftLayer/API.py
+++ b/SoftLayer/API.py
@@ -34,6 +34,7 @@ VALID_CALL_ARGS = set((
     'raw_headers',
     'limit',
     'offset',
+    'verify',
 ))
 
 
@@ -84,14 +85,24 @@ def create_client_from_env(username=None,
                                           proxy=proxy,
                                           config_file=config_file)
 
-    # Default the transport to use XMLRPC
     if transport is None:
-        transport = transports.XmlRpcTransport(
-            endpoint_url=settings.get('endpoint_url'),
-            proxy=settings.get('proxy'),
-            timeout=settings.get('timeout'),
-            user_agent=user_agent,
-        )
+        url = settings.get('endpoint_url')
+        if url is not None and '/rest' in url:
+            # If this looks like a rest endpoint, use the rest transport
+            transport = transports.RestTransport(
+                endpoint_url=settings.get('endpoint_url'),
+                proxy=settings.get('proxy'),
+                timeout=settings.get('timeout'),
+                user_agent=user_agent,
+            )
+        else:
+            # Default the transport to use XMLRPC
+            transport = transports.XmlRpcTransport(
+                endpoint_url=settings.get('endpoint_url'),
+                proxy=settings.get('proxy'),
+                timeout=settings.get('timeout'),
+                user_agent=user_agent,
+            )
 
     # If we have enough information to make an auth driver, let's do it
     if auth is None and settings.get('username') and settings.get('api_key'):
@@ -174,15 +185,22 @@ class BaseClient(object):
         return Service(self, name)
 
     def call(self, service, method, *args, **kwargs):
-        """Make a SoftLayer API call
+        """Make a SoftLayer API call.
 
-        :param service: the name of the SoftLayer API service
         :param method: the method to call on the service
-        :param \\*args: same optional arguments that ``Service.call`` takes
-        :param \\*\\*kwargs: same optional keyword arguments that
-                           ``Service.call`` takes
-
-        :param service: the name of the SoftLayer API service
+        :param \\*args: (optional) arguments for the remote call
+        :param id: (optional) id for the resource
+        :param mask: (optional) object mask
+        :param dict filter: (optional) filter dict
+        :param dict headers: (optional) optional XML-RPC headers
+        :param boolean compress: (optional) Enable/Disable HTTP compression
+        :param dict raw_headers: (optional) HTTP transport headers
+        :param int limit: (optional) return at most this many results
+        :param int offset: (optional) offset results by this many
+        :param boolean iter: (optional) if True, returns a generator with the
+                             results
+        :param bool verify: verify SSL cert
+        :param cert: client certificate path
 
         Usage:
             >>> import SoftLayer
@@ -222,6 +240,7 @@ class BaseClient(object):
         request.filter = kwargs.get('filter')
         request.limit = kwargs.get('limit')
         request.offset = kwargs.get('offset')
+        request.verify = kwargs.get('verify')
 
         if self.auth:
             extra_headers = self.auth.get_headers()
@@ -314,22 +333,15 @@ class Service(object):
         self.name = name
 
     def call(self, name, *args, **kwargs):
-        """Make a SoftLayer API call.
+        """Make a SoftLayer API call
 
+        :param service: the name of the SoftLayer API service
         :param method: the method to call on the service
-        :param \\*args: (optional) arguments for the remote call
-        :param id: (optional) id for the resource
-        :param mask: (optional) object mask
-        :param dict filter: (optional) filter dict
-        :param dict headers: (optional) optional XML-RPC headers
-        :param boolean compress: (optional) Enable/Disable HTTP compression
-        :param dict raw_headers: (optional) HTTP transport headers
-        :param int limit: (optional) return at most this many results
-        :param int offset: (optional) offset results by this many
-        :param boolean iter: (optional) if True, returns a generator with the
-                             results
-        :param bool verify: verify SSL cert
-        :param cert: client certificate path
+        :param \\*args: same optional arguments that ``BaseClient.call`` takes
+        :param \\*\\*kwargs: same optional keyword arguments that
+                           ``BaseClient.call`` takes
+
+        :param service: the name of the SoftLayer API service
 
         Usage:
             >>> import SoftLayer
diff --git a/SoftLayer/CLI/block/access/__init__.py b/SoftLayer/CLI/block/access/__init__.py
new file mode 100644
index 0000000..050b393
--- /dev/null
+++ b/SoftLayer/CLI/block/access/__init__.py
@@ -0,0 +1 @@
+"""Block Storage Access Control."""
diff --git a/SoftLayer/CLI/block/access/authorize.py b/SoftLayer/CLI/block/access/authorize.py
new file mode 100644
index 0000000..df76b60
--- /dev/null
+++ b/SoftLayer/CLI/block/access/authorize.py
@@ -0,0 +1,45 @@
+"""Authorizes hosts on a specific block volume."""
+# :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('volume_id')
+ at click.option('--hardware-id', '-h', multiple=True,
+              help='The id of one SoftLayer_Hardware to authorize')
+ at click.option('--virtual-id', '-v', multiple=True,
+              help='The id of one SoftLayer_Virtual_Guest to authorize')
+ at click.option('--ip-address-id', '-i', multiple=True,
+              help='The id of one SoftLayer_Network_Subnet_IpAddress'
+              ' to authorize')
+ at click.option('--ip-address', multiple=True,
+              help='An IP address to authorize')
+ at environment.pass_env
+def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address):
+    """Authorizes hosts to access a given volume"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    ip_address_id_list = list(ip_address_id)
+
+    # Convert actual IP Addresses to their SoftLayer ids
+    if ip_address is not None:
+        network_manager = SoftLayer.NetworkManager(env.client)
+        for ip_address_value in ip_address:
+            ip_address_object = network_manager.ip_lookup(ip_address_value)
+            if ip_address_object == "":
+                click.echo("IP Address not found on your account.  " +
+                           "Please confirm IP and try again.")
+                raise exceptions.ArgumentError('Incorrect IP Address')
+            else:
+                ip_address_id_list.append(ip_address_object['id'])
+
+    block_manager.authorize_host_to_volume(volume_id,
+                                           hardware_id,
+                                           virtual_id,
+                                           ip_address_id_list)
+
+    # If no exception was raised, the command succeeded
+    click.echo('The specified hosts were authorized to access %s' % volume_id)
diff --git a/SoftLayer/CLI/block/access/list.py b/SoftLayer/CLI/block/access/list.py
new file mode 100644
index 0000000..b011e26
--- /dev/null
+++ b/SoftLayer/CLI/block/access/list.py
@@ -0,0 +1,38 @@
+"""List hosts with access to block volume."""
+# :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
+from SoftLayer.CLI import storage_utils
+
+
+ at click.command()
+ at click.argument('volume_id')
+ at click.option('--sortby', help='Column to sort by', default='name')
+ at click.option('--columns',
+              callback=column_helper.get_formatter(storage_utils.COLUMNS),
+              help='Columns to display. Options: {0}'.format(
+                  ', '.join(column.name for column in storage_utils.COLUMNS)),
+              default=','.join(storage_utils.DEFAULT_COLUMNS))
+ at environment.pass_env
+def cli(env, columns, sortby, volume_id):
+    """List ACLs."""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    access_list = block_manager.get_block_volume_access_list(
+        volume_id=volume_id)
+    table = formatting.Table(columns.columns)
+    table.sortby = sortby
+
+    for key, type_name in [('allowedVirtualGuests', 'VIRTUAL'),
+                           ('allowedHardware', 'HARDWARE'),
+                           ('allowedSubnets', 'SUBNET'),
+                           ('allowedIpAddresses', 'IP')]:
+        for obj in access_list.get(key, []):
+            obj['type'] = type_name
+            table.add_row([value or formatting.blank()
+                           for value in columns.row(obj)])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/block/access/revoke.py b/SoftLayer/CLI/block/access/revoke.py
new file mode 100644
index 0000000..c2284be
--- /dev/null
+++ b/SoftLayer/CLI/block/access/revoke.py
@@ -0,0 +1,41 @@
+"""Revokes hosts' access on a specific block 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.option('--hardware-id', '-h', multiple=True,
+              help='The id of one SoftLayer_Hardware'
+              ' to revoke authorization')
+ at click.option('--virtual-id', '-v', multiple=True,
+              help='The id of one SoftLayer_Virtual_Guest'
+              ' to revoke authorization')
+ at click.option('--ip-address-id', '-i', multiple=True,
+              help='The id of one SoftLayer_Network_Subnet_IpAddress'
+              ' to revoke authorization')
+ at click.option('--ip-address', multiple=True,
+              help='An IP address to revoke authorization')
+ at environment.pass_env
+def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address):
+    """Revokes authorization for hosts accessing a given volume"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    ip_address_id_list = list(ip_address_id)
+
+    # Convert actual IP Addresses to their SoftLayer ids
+    if ip_address is not None:
+        network_manager = SoftLayer.NetworkManager(env.client)
+        for ip_address_value in ip_address:
+            ip_address_object = network_manager.ip_lookup(ip_address_value)
+            ip_address_id_list.append(ip_address_object['id'])
+
+    block_manager.deauthorize_host_to_volume(volume_id,
+                                             hardware_id,
+                                             virtual_id,
+                                             ip_address_id_list)
+
+    # If no exception was raised, the command succeeded
+    click.echo('Access to %s was revoked for the specified hosts' % volume_id)
diff --git a/SoftLayer/CLI/block/cancel.py b/SoftLayer/CLI/block/cancel.py
index a23cee0..0311845 100644
--- a/SoftLayer/CLI/block/cancel.py
+++ b/SoftLayer/CLI/block/cancel.py
@@ -25,4 +25,15 @@ def cli(env, volume_id, reason, immediate):
     if not (env.skip_confirmations or formatting.no_going_back(volume_id)):
         raise exceptions.CLIAbort('Aborted')
 
-    block_storage_manager.cancel_block_volume(volume_id, reason, immediate)
+    cancelled = block_storage_manager.cancel_block_volume(volume_id,
+                                                          reason, immediate)
+
+    if cancelled:
+        if immediate:
+            click.echo('Block volume with id %s has been marked'
+                       ' for immediate cancellation' % volume_id)
+        else:
+            click.echo('Block volume with id %s has been marked'
+                       ' for cancellation' % volume_id)
+    else:
+        click.echo('Unable to cancel block volume %s' % volume_id)
diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py
index c98736e..51a1fbd 100644
--- a/SoftLayer/CLI/block/detail.py
+++ b/SoftLayer/CLI/block/detail.py
@@ -51,9 +51,52 @@ def cli(env, volume_id):
             'Snapshot Capacity (GB)',
             block_volume['snapshotCapacityGb'],
         ])
-        table.add_row([
-            'Snapshot Used (Bytes)',
-            block_volume['parentVolume']['snapshotSizeBytes'],
-        ])
+        if 'snapshotSizeBytes' in block_volume['parentVolume']:
+            table.add_row([
+                'Snapshot Used (Bytes)',
+                block_volume['parentVolume']['snapshotSizeBytes'],
+            ])
+
+    table.add_row(['# of Active Transactions', "%i"
+                   % block_volume['activeTransactionCount']])
+
+    if block_volume['activeTransactions']:
+        for trans in block_volume['activeTransactions']:
+            table.add_row([
+                'Ongoing Transactions',
+                trans['transactionStatus']['friendlyName']])
+
+    table.add_row(['Replicant Count', "%u"
+                   % block_volume['replicationPartnerCount']])
+
+    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']])
+        else:
+            table.add_row(['Replication Status', "%s"
+                           % block_volume['replicationStatus']])
+
+        replicant_list = []
+        for replicant in block_volume['replicationPartners']:
+            replicant_table = formatting.Table(['Replicant ID',
+                                                replicant['id']])
+            replicant_table.add_row([
+                'Volume Name',
+                replicant['username']])
+            replicant_table.add_row([
+                'Target IP',
+                replicant['serviceResourceBackendIpAddress']])
+            replicant_table.add_row([
+                'Data Center',
+                replicant['serviceResource']['datacenter']['name']])
+            replicant_table.add_row([
+                'Schedule',
+                replicant['replicationSchedule']['type']['keyname']])
+            replicant_list.append(replicant_table)
+        table.add_row(['Replicant Volumes', replicant_list])
 
     env.fout(table)
diff --git a/SoftLayer/CLI/block/list.py b/SoftLayer/CLI/block/list.py
index 40edcb7..41e09ca 100644
--- a/SoftLayer/CLI/block/list.py
+++ b/SoftLayer/CLI/block/list.py
@@ -16,12 +16,21 @@ COLUMNS = [
                          mask="serviceResource.datacenter.name"),
     column_helper.Column(
         'storage_type',
-        lambda b: b['storageType']['keyName'].split('_').pop(0),
+        lambda b: b['storageType']['keyName'].split('_').pop(0)
+        if 'storageType' in b and 'keyName' in b['storageType']
+        and isinstance(b['storageType']['keyName'], str)
+        else '-',
         mask="storageType.keyName"),
     column_helper.Column('capacity_gb', ('capacityGb',), mask="capacityGb"),
     column_helper.Column('bytes_used', ('bytesUsed',), mask="bytesUsed"),
     column_helper.Column('ip_addr', ('serviceResourceBackendIpAddress',),
                          mask="serviceResourceBackendIpAddress"),
+    column_helper.Column('lunId', ('lunId',), mask="lunId"),
+    column_helper.Column('active_transactions', ('activeTransactionCount',),
+                         mask="activeTransactionCount"),
+    column_helper.Column(
+        'created_by',
+        ('billingItem', 'orderItem', 'order', 'userRecord', 'username')),
 ]
 
 DEFAULT_COLUMNS = [
@@ -31,7 +40,9 @@ DEFAULT_COLUMNS = [
     'storage_type',
     'capacity_gb',
     'bytes_used',
-    'ip_addr'
+    'ip_addr',
+    'lunId',
+    'active_transactions'
 ]
 
 
diff --git a/SoftLayer/CLI/block/order.py b/SoftLayer/CLI/block/order.py
index f184007..22430e4 100644
--- a/SoftLayer/CLI/block/order.py
+++ b/SoftLayer/CLI/block/order.py
@@ -7,17 +7,17 @@ from SoftLayer.CLI import environment
 from SoftLayer.CLI import exceptions
 
 
-CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
+CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
 
 
 @click.command(context_settings=CONTEXT_SETTINGS)
 @click.option('--storage-type',
-              help='Type of storage volume',
+              help='Type of block storage volume',
               type=click.Choice(['performance', 'endurance']),
               required=True)
 @click.option('--size',
               type=int,
-              help='Size of storage volume in GB',
+              help='Size of block storage volume in GB',
               required=True)
 @click.option('--iops',
               type=int,
@@ -42,8 +42,14 @@ CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
 @click.option('--location',
               help='Datacenter short name (e.g.: dal09)',
               required=True)
+ at click.option('--snapshot-size',
+              type=int,
+              help='Optional parameter for ordering snapshot '
+              'space along with endurance block storage; specifies '
+              'the size (in GB) of snapshot space to order')
 @environment.pass_env
-def cli(env, storage_type, size, iops, tier, os_type, location):
+def cli(env, storage_type, size, iops, tier, os_type,
+        location, snapshot_size):
     """Order a block storage volume."""
     block_manager = SoftLayer.BlockStorageManager(env.client)
     storage_type = storage_type.lower()
@@ -62,6 +68,12 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
                 'Option --iops must be a multiple of 100'
             )
 
+        if snapshot_size is not None:
+            raise exceptions.CLIAbort(
+                'Option --snapshot-size not allowed for performance volumes.'
+                ' Snapshots are only available for endurance storage.'
+            )
+
         try:
             order = block_manager.order_block_volume(
                 storage_type='performance_storage_iscsi',
@@ -84,7 +96,8 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
                 location=location,
                 size=size,
                 tier_level=float(tier),
-                os_type=os_type
+                os_type=os_type,
+                snapshot_size=snapshot_size
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/block/replication/__init__.py b/SoftLayer/CLI/block/replication/__init__.py
new file mode 100644
index 0000000..ac0a44b
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/__init__.py
@@ -0,0 +1 @@
+"""Block Storage Replication Control."""
diff --git a/SoftLayer/CLI/block/replication/failback.py b/SoftLayer/CLI/block/replication/failback.py
new file mode 100644
index 0000000..3887c29
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/failback.py
@@ -0,0 +1,25 @@
+"""Failback from a replicant 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.option('--replicant-id', help="ID of the replicant volume")
+ at environment.pass_env
+def cli(env, volume_id, replicant_id):
+    """Failback a block volume from the given replicant volume."""
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    success = block_storage_manager.failback_from_replicant(
+        volume_id,
+        replicant_id
+    )
+
+    if success:
+        click.echo("Failback from replicant is now in progress.")
+    else:
+        click.echo("Failback operation could not be initiated.")
diff --git a/SoftLayer/CLI/block/replication/failover.py b/SoftLayer/CLI/block/replication/failover.py
new file mode 100644
index 0000000..545175c
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/failover.py
@@ -0,0 +1,30 @@
+"""Failover to a replicant 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.option('--replicant-id', help="ID of the replicant volume")
+ at click.option('--immediate',
+              is_flag=True,
+              default=False,
+              help="Failover to replicant immediately.")
+ at environment.pass_env
+def cli(env, volume_id, replicant_id, immediate):
+    """Failover a block volume to the given replicant volume."""
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    success = block_storage_manager.failover_to_replicant(
+        volume_id,
+        replicant_id,
+        immediate
+    )
+
+    if success:
+        click.echo("Failover to replicant is now in progress.")
+    else:
+        click.echo("Failover operation could not be initiated.")
diff --git a/SoftLayer/CLI/block/replication/order.py b/SoftLayer/CLI/block/replication/order.py
new file mode 100644
index 0000000..c25a8be
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/order.py
@@ -0,0 +1,65 @@
+"""Order a block storage replica volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
+
+
+ at click.command(context_settings=CONTEXT_SETTINGS)
+ at click.argument('volume_id')
+ at click.option('--snapshot-schedule', '-s',
+              help='Snapshot schedule to use for replication, '
+              '(HOURLY | DAILY | WEEKLY)',
+              required=True,
+              type=click.Choice(['HOURLY', 'DAILY', 'WEEKLY']))
+ at click.option('--location', '-l',
+              help='Short name of the data center for the replicant '
+              '(e.g.: dal09)',
+              required=True)
+ at click.option('--tier',
+              help='Endurance Storage Tier (IOPS per GB) of the primary'
+              ' volume for which a replicant is ordered [optional]',
+              type=click.Choice(['0.25', '2', '4']))
+ at click.option('--os-type',
+              help='Operating System Type (e.g.: LINUX) of the primary'
+              ' volume for which a replica is ordered [optional]',
+              type=click.Choice([
+                  'HYPER_V',
+                  'LINUX',
+                  'VMWARE',
+                  'WINDOWS_2008',
+                  'WINDOWS_GPT',
+                  'WINDOWS',
+                  'XEN']))
+ at environment.pass_env
+def cli(env, volume_id, snapshot_schedule, location, tier, os_type):
+    """Order a block storage replica volume."""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    if tier is not None:
+        tier = float(tier)
+
+    try:
+        order = block_manager.order_replicant_volume(
+            volume_id,
+            snapshot_schedule=snapshot_schedule,
+            location=location,
+            tier=tier,
+            os_type=os_type,
+        )
+    except ValueError as ex:
+        raise exceptions.ArgumentError(str(ex))
+
+    if 'placedOrder' in order.keys():
+        click.echo("Order #{0} placed successfully!".format(
+            order['placedOrder']['id']))
+        for item in order['placedOrder']['items']:
+            click.echo(" > %s" % item['description'])
+    else:
+        click.echo("Order could not be placed! Please verify your options " +
+                   "and try again.")
diff --git a/SoftLayer/CLI/block/snapshot/__init__.py b/SoftLayer/CLI/block/snapshot/__init__.py
new file mode 100644
index 0000000..eb4d41b
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/__init__.py
@@ -0,0 +1 @@
+"""Block Storage Snapshot Control."""
diff --git a/SoftLayer/CLI/block/snapshot/cancel.py b/SoftLayer/CLI/block/snapshot/cancel.py
new file mode 100644
index 0000000..7a6150b
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/cancel.py
@@ -0,0 +1,40 @@
+"""Cancel a snapshot space subscription."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
+
+
+ at click.command()
+ at click.argument('volume-id')
+ at click.option('--reason', help="An optional reason for cancellation")
+ at click.option('--immediate',
+              is_flag=True,
+              help="Cancels the snapshot space immediately instead "
+                   "of on the billing anniversary")
+ at environment.pass_env
+def cli(env, volume_id, reason, immediate):
+    """Cancel existing snapshot space for a given volume."""
+
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    if not (env.skip_confirmations or formatting.no_going_back(volume_id)):
+        raise exceptions.CLIAbort('Aborted')
+
+    cancelled = block_storage_manager.cancel_snapshot_space(
+        volume_id, reason, immediate)
+
+    if cancelled:
+        if immediate:
+            click.echo('Block volume with id %s has been marked'
+                       ' for immediate snapshot cancellation' % volume_id)
+        else:
+            click.echo('Block volume with id %s has been marked'
+                       ' for snapshot cancellation' % volume_id)
+    else:
+        click.echo('Unable to cancel snapshot space for block volume %s'
+                   % volume_id)
diff --git a/SoftLayer/CLI/block/snapshot/create.py b/SoftLayer/CLI/block/snapshot/create.py
new file mode 100644
index 0000000..68dc976
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/create.py
@@ -0,0 +1,24 @@
+"""Create a block storage snapshot."""
+# :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.option('--notes', '-n',
+              help='Notes to set on the new snapshot')
+ at environment.pass_env
+def cli(env, volume_id, notes):
+    """Creates a snapshot on a given volume"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    snapshot = block_manager.create_snapshot(volume_id, notes=notes)
+
+    if 'id' in snapshot:
+        click.echo('New snapshot created with id: %s' % snapshot['id'])
+    else:
+        click.echo('Error occurred while creating snapshot.\n'
+                   'Ensure volume is not failed over or in another '
+                   'state which prevents taking snapshots.')
diff --git a/SoftLayer/CLI/block/snapshot_delete.py b/SoftLayer/CLI/block/snapshot/delete.py
similarity index 65%
copy from SoftLayer/CLI/block/snapshot_delete.py
copy to SoftLayer/CLI/block/snapshot/delete.py
index 6daa3f4..229a5d7 100644
--- a/SoftLayer/CLI/block/snapshot_delete.py
+++ b/SoftLayer/CLI/block/snapshot/delete.py
@@ -1,4 +1,4 @@
-"""Create a block storage snapshot."""
+"""Delete a block storage snapshot."""
 # :license: MIT, see LICENSE for more details.
 
 import click
@@ -12,4 +12,7 @@ from SoftLayer.CLI import environment
 def cli(env, snapshot_id):
     """Deletes a snapshot on a given volume"""
     block_manager = SoftLayer.BlockStorageManager(env.client)
-    block_manager.delete_snapshot(snapshot_id)
+    deleted = block_manager.delete_snapshot(snapshot_id)
+
+    if deleted:
+        click.echo('Snapshot %s deleted' % snapshot_id)
diff --git a/SoftLayer/CLI/block/snapshot/disable.py b/SoftLayer/CLI/block/snapshot/disable.py
new file mode 100644
index 0000000..f34d348
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/disable.py
@@ -0,0 +1,29 @@
+"""Disable scheduled snapshots of a specific volume"""
+# :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('volume_id')
+ at click.option('--schedule-type',
+              help='Snapshot schedule [HOURLY|DAILY|WEEKLY]',
+              required=True)
+ at environment.pass_env
+def cli(env, volume_id, schedule_type):
+    """Disables snapshots on the specified schedule for a given volume"""
+
+    if (schedule_type != 'HOURLY' and schedule_type != 'DAILY'
+            and schedule_type != 'WEEKLY'):
+        raise exceptions.CLIAbort(
+            '--schedule-type must be HOURLY, DAILY, or WEEKLY')
+
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    disabled = block_manager.disable_snapshots(volume_id, schedule_type)
+
+    if disabled:
+        click.echo('%s snapshots have been disabled for volume %s'
+                   % (schedule_type, volume_id))
diff --git a/SoftLayer/CLI/block/snapshot/enable.py b/SoftLayer/CLI/block/snapshot/enable.py
new file mode 100644
index 0000000..6ade946
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/enable.py
@@ -0,0 +1,58 @@
+# snapshot_enable.py
+"""Create a block storage snapshot [ENABLE]."""
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+ at click.command()
+ at click.argument('volume_id')
+ at click.option('--schedule-type',
+              help='Snapshot schedule [HOURLY|DAILY|WEEKLY]',
+              required=True)
+ at click.option('--retention-count',
+              help='Number of snapshots to retain',
+              required=True)
+ at click.option('--minute',
+              help='Minute of the day when snapshots should be taken',
+              default=0)
+ at click.option('--hour',
+              help='Hour of the day when snapshots should be taken',
+              default=0)
+ at click.option('--day-of-week',
+              help='Day of the week when snapshots should be taken',
+              default='SUNDAY')
+ at environment.pass_env
+def cli(env, volume_id, schedule_type, retention_count,
+        minute, hour, day_of_week):
+    """Enables snapshots for a given volume on the specified schedule"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    valid_schedule_types = {'HOURLY', 'DAILY', 'WEEKLY'}
+    valid_days = {'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY',
+                  'FRIDAY', 'SATURDAY'}
+
+    if schedule_type not in valid_schedule_types:
+        raise exceptions.CLIAbort(
+            '--schedule-type must be HOURLY, DAILY, or WEEKLY, not '
+            + schedule_type)
+
+    if minute < 0 or minute > 59:
+        raise exceptions.CLIAbort(
+            '--minute value must be between 0 and 59')
+    if hour < 0 or hour > 23:
+        raise exceptions.CLIAbort(
+            '--hour value must be between 0 and 23')
+    if day_of_week not in valid_days:
+        raise exceptions.CLIAbort(
+            '--day_of_week value must be a valid day (ex: SUNDAY)')
+
+    enabled = block_manager.enable_snapshots(volume_id, schedule_type,
+                                             retention_count, minute,
+                                             hour, day_of_week)
+
+    if enabled:
+        click.echo('%s snapshots have been enabled for volume %s'
+                   % (schedule_type, volume_id))
diff --git a/SoftLayer/CLI/block/snapshot_list.py b/SoftLayer/CLI/block/snapshot/list.py
similarity index 65%
copy from SoftLayer/CLI/block/snapshot_list.py
copy to SoftLayer/CLI/block/snapshot/list.py
index 7ffea3f..b47f594 100644
--- a/SoftLayer/CLI/block/snapshot_list.py
+++ b/SoftLayer/CLI/block/snapshot/list.py
@@ -9,17 +9,12 @@ from SoftLayer.CLI import formatting
 
 
 COLUMNS = [
-    column_helper.Column(
-        'id',
-        ('snapshots', 'id',),
-        mask='snapshots.id'),
-    column_helper.Column('name', ('snapshots', 'notes',),
-                         mask='snapshots.notes'),
-    column_helper.Column('created',
-                         ('snapshots', 'snapshotCreationTimestamp',),
-                         mask='snapshots.snapshotCreationTimestamp'),
-    column_helper.Column('size_bytes', ('snapshots', 'snapshotSizeBytes',),
-                         mask='snapshots.snapshotSizeBytes'),
... 7186 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