Repository: softlayer/softlayer-python
Pull request #1882 (2023-03-17 16:07:43)

Unable to determine file\block storage limits per datacenter
[#issue1879](https://github.com/softlayer/softlayer-python/issues/1879)

![image](https://user-images.githubusercontent.com/22535437/225958359-646fce31-a26a-4717-b944-586780705809.png)

![image](https://user-images.githubusercontent.com/22535437/225958439-40f1bc64-d44e-496c-a31d-1a25e6dd6215.png)



Patch:
diff --git a/SoftLayer/CLI/autoscale/__init__.py b/SoftLayer/CLI/autoscale/__init__.py
new file mode 100644
index 00000000..81d12638
--- /dev/null
+++ b/SoftLayer/CLI/autoscale/__init__.py
@@ -0,0 +1,1 @@
+"""Autoscale"""
diff --git a/SoftLayer/CLI/autoscale/delete.py b/SoftLayer/CLI/autoscale/delete.py
new file mode 100644
index 00000000..082d5c61
--- /dev/null
+++ b/SoftLayer/CLI/autoscale/delete.py
@@ -0,0 +1,27 @@
+"""Delete autoscale."""
+# :license: MIT, see LICENSE for more details.
+import click
+import SoftLayer
+
+from SoftLayer.CLI.command import SLCommand as SLCommand
+from SoftLayer.CLI import environment
+from SoftLayer.managers.autoscale import AutoScaleManager
+
+
+@click.command(cls=SLCommand)
+@click.argument('identifier')
+@environment.pass_env
+def cli(env, identifier):
+    """Delete this group and destroy all members of it.
+
+    Example: slcli autoscale delete autoscaleId
+
+    """
+
+    autoscale = AutoScaleManager(env.client)
+
+    try:
+        autoscale.delete(identifier)
+        click.secho("%s deleted successfully" % identifier, fg='green')
+    except SoftLayer.SoftLayerAPIError as ex:
+        click.secho("Failed to delete %s\n%s" % (identifier, ex), fg='red')
diff --git a/SoftLayer/CLI/autoscale/detail.py b/SoftLayer/CLI/autoscale/detail.py
new file mode 100644
index 00000000..9d2d36ad
--- /dev/null
+++ b/SoftLayer/CLI/autoscale/detail.py
@@ -0,0 +1,93 @@
+"""Get details of an Autoscale groups."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI.command import SLCommand as SLCommand
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer.managers.autoscale import AutoScaleManager
+from SoftLayer import utils
+
+
+@click.command(cls=SLCommand)
+@click.argument('identifier')
+@environment.pass_env
+def cli(env, identifier):
+    """Get details of an Autoscale group."""
+
+    autoscale = AutoScaleManager(env.client)
+    group = autoscale.details(identifier)
+
+    # Group Config Table
+    table = formatting.KeyValueTable(["Group", "Value"])
+    table.align['Group'] = 'l'
+    table.align['Value'] = 'l'
+
+    table.add_row(['Id', group.get('id')])
+    # Ideally we would use regionalGroup->preferredDatacenter, but that generates an error.
+    table.add_row(['Datacenter', group['regionalGroup']['locations'][0]['longName']])
+    table.add_row(['Termination', utils.lookup(group, 'terminationPolicy', 'name')])
+    table.add_row(['Minimum Members', group.get('minimumMemberCount')])
+    table.add_row(['Maximum Members', group.get('maximumMemberCount')])
+    table.add_row(['Current Members', group.get('virtualGuestMemberCount')])
+    table.add_row(['Cooldown', "{} seconds".format(group.get('cooldown'))])
+    table.add_row(['Last Action', utils.clean_time(group.get('lastActionDate'))])
+
+    for network in group.get('networkVlans', []):
+        network_type = utils.lookup(network, 'networkVlan', 'networkSpace')
+        router = utils.lookup(network, 'networkVlan', 'primaryRouter', 'hostname')
+        vlan_number = utils.lookup(network, 'networkVlan', 'vlanNumber')
+        vlan_name = "{}.{}".format(router, vlan_number)
+        table.add_row([network_type, vlan_name])
+
+    env.fout(table)
+
+    # Template Config Table
+    config_table = formatting.KeyValueTable(["Template", "Value"])
+    config_table.align['Template'] = 'l'
+    config_table.align['Value'] = 'l'
+
+    template = group.get('virtualGuestMemberTemplate')
+
+    config_table.add_row(['Hostname', template.get('hostname')])
+    config_table.add_row(['Domain', template.get('domain')])
+    config_table.add_row(['Core', template.get('startCpus')])
+    config_table.add_row(['Ram', template.get('maxMemory')])
+    network = template.get('networkComponents')
+    config_table.add_row(['Network', network[0]['maxSpeed'] if network else 'Default'])
+    ssh_keys = template.get('sshKeys', [])
+    ssh_manager = SoftLayer.SshKeyManager(env.client)
+    for key in ssh_keys:
+        # Label isn't included when retrieved from the AutoScale group...
+        ssh_key = ssh_manager.get_key(key.get('id'))
+        config_table.add_row(['SSH Key {}'.format(ssh_key.get('id')), ssh_key.get('label')])
+    disks = template.get('blockDevices', [])
+    disk_type = "Local" if template.get('localDiskFlag') else "SAN"
+
+    for disk in disks:
+        disk_image = disk.get('diskImage')
+        config_table.add_row(['{} Disk {}'.format(disk_type, disk.get('device')), disk_image.get('capacity')])
+    config_table.add_row(['OS', template.get('operatingSystemReferenceCode')])
+    config_table.add_row(['Post Install', template.get('postInstallScriptUri') or 'None'])
+
+    env.fout(config_table)
+
+    # Policy Config Table
+    policy_table = formatting.KeyValueTable(["Policy", "Cooldown"])
+    policies = group.get('policies', [])
+    for policy in policies:
+        policy_table.add_row([policy.get('name'), policy.get('cooldown') or group.get('cooldown')])
+
+    env.fout(policy_table)
+
+    # Active Guests
+    member_table = formatting.Table(['Id', 'Hostname', 'Created'], title="Active Guests")
+    guests = group.get('virtualGuestMembers', [])
+    for guest in guests:
+        real_guest = guest.get('virtualGuest')
+        member_table.add_row([
+            real_guest.get('id'), real_guest.get('hostname'), utils.clean_time(real_guest.get('provisionDate'))
+        ])
+    env.fout(member_table)
diff --git a/SoftLayer/CLI/autoscale/list.py b/SoftLayer/CLI/autoscale/list.py
new file mode 100644
index 00000000..bcc1b244
--- /dev/null
+++ b/SoftLayer/CLI/autoscale/list.py
@@ -0,0 +1,30 @@
+"""List Autoscale groups."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+from SoftLayer.CLI.command import SLCommand as SLCommand
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer.managers.autoscale import AutoScaleManager
+from SoftLayer import utils
+
+
+@click.command(cls=SLCommand)
+@environment.pass_env
+def cli(env):
+    """List all Autoscale Groups on your account."""
+
+    autoscale = AutoScaleManager(env.client)
+    groups = autoscale.list()
+
+    table = formatting.Table(["Id", "Name", "Status", "Min/Max", "Running"])
+    table.align['Name'] = 'l'
+    for group in groups:
+        status = utils.lookup(group, 'status', 'name')
+        min_max = "{}/{}".format(group.get('minimumMemberCount'), group.get('maximumMemberCount'))
+        table.add_row([
+            group.get('id'), group.get('name'), status, min_max, group.get('virtualGuestMemberCount')
+        ])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/block/limit.py b/SoftLayer/CLI/block/limit.py
index 815ae7f9..30242487 100644
--- a/SoftLayer/CLI/block/limit.py
+++ b/SoftLayer/CLI/block/limit.py
@@ -15,17 +15,30 @@
 
 @click.command(cls=SoftLayer.CLI.command.SLCommand, )
 @click.option('--sortby', help='Column to sort by', default='Datacenter')
+@click.option('--datacenter', '-d', help='Filter by datacenter')
 @environment.pass_env
-def cli(env, sortby):
+def cli(env, sortby, datacenter):
     """List number of block storage volumes limit per datacenter."""
     block_manager = SoftLayer.BlockStorageManager(env.client)
     block_volumes = block_manager.list_block_volume_limit()
 
     table = formatting.KeyValueTable(DEFAULT_COLUMNS)
     table.sortby = sortby
-    for volume in block_volumes:
-        datacenter_name = volume['datacenterName']
-        maximum_available_count = volume['maximumAvailableCount']
-        provisioned_count = volume['provisionedCount']
-        table.add_row([datacenter_name, maximum_available_count, provisioned_count])
+
+    for volumen in block_volumes:
+        if datacenter:
+            if volumen.get('datacenterName') != '':
+                if volumen.get('datacenterName') == datacenter:
+                    table.add_row([volumen.get('datacenterName'),
+                                   volumen.get('maximumAvailableCount'),
+                                   volumen.get('provisionedCount')])
+                    break
+        else:
+            if volumen.get('datacenterName') != '':
+                table.add_row([volumen.get('datacenterName'), volumen.get('maximumAvailableCount'),
+                               volumen.get('provisionedCount')])
+            else:
+                table.add_row([' - ',
+                               volumen.get('maximumAvailableCount'),
+                               volumen.get('provisionedCount')])
     env.fout(table)
diff --git a/SoftLayer/CLI/file/limit.py b/SoftLayer/CLI/file/limit.py
index 7fa16b7e..e0243ddc 100644
--- a/SoftLayer/CLI/file/limit.py
+++ b/SoftLayer/CLI/file/limit.py
@@ -15,17 +15,29 @@
 
 @click.command(cls=SoftLayer.CLI.command.SLCommand, )
 @click.option('--sortby', help='Column to sort by', default='Datacenter')
+@click.option('--datacenter', '-d', help='Filter by datacenter')
 @environment.pass_env
-def cli(env, sortby):
+def cli(env, sortby, datacenter):
     """List number of block storage volumes limit per datacenter."""
     file_manager = SoftLayer.FileStorageManager(env.client)
     file_volumes = file_manager.list_file_volume_limit()
 
     table = formatting.KeyValueTable(DEFAULT_COLUMNS)
     table.sortby = sortby
-    for volume in file_volumes:
-        datacenter_name = volume['datacenterName']
-        maximum_available_count = volume['maximumAvailableCount']
-        provisioned_count = volume['provisionedCount']
-        table.add_row([datacenter_name, maximum_available_count, provisioned_count])
+    for volumen in file_volumes:
+        if datacenter:
+            if volumen.get('datacenterName') != '':
+                if volumen.get('datacenterName') == datacenter:
+                    table.add_row([volumen.get('datacenterName'),
+                                   volumen.get('maximumAvailableCount'),
+                                   volumen.get('provisionedCount')])
+                    break
+        else:
+            if volumen.get('datacenterName') != '':
+                table.add_row([volumen.get('datacenterName'), volumen.get('maximumAvailableCount'),
+                               volumen.get('provisionedCount')])
+            else:
+                table.add_row([' - ',
+                               volumen.get('maximumAvailableCount'),
+                               volumen.get('provisionedCount')])
     env.fout(table)
diff --git a/SoftLayer/CLI/image/share.py b/SoftLayer/CLI/image/share.py
deleted file mode 100644
index e548bdbe..00000000
--- a/SoftLayer/CLI/image/share.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""Share an image template with another account."""
-# :license: MIT, see LICENSE for more details.
-
-import click
-
-import SoftLayer
-from SoftLayer.CLI import environment
-from SoftLayer.CLI import helpers
-
-
-@click.command(cls=SoftLayer.CLI.command.SLCommand, )
-@click.argument('identifier')
-@click.option('--account-id', help='Account Id for another account to share image template', required=True)
-@environment.pass_env
-def cli(env, identifier, account_id):
-    """Share an image template with another account."""
-
-    image_mgr = SoftLayer.ImageManager(env.client)
-    image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image')
-    shared_image = image_mgr.share_image(image_id, account_id)
-
-    if shared_image:
-        env.fout("Image template {} was shared to account {}.".format(identifier, account_id))
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 9d77eee7..b1cb139d 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -209,7 +209,6 @@
     ('image:import', 'SoftLayer.CLI.image.import:cli'),
     ('image:export', 'SoftLayer.CLI.image.export:cli'),
     ('image:datacenter', 'SoftLayer.CLI.image.datacenter:cli'),
-    ('image:share', 'SoftLayer.CLI.image.share:cli'),
     ('image:share-deny', 'SoftLayer.CLI.image.share_deny:cli'),
 
     ('ipsec', 'SoftLayer.CLI.vpn.ipsec'),
@@ -398,6 +397,11 @@
     ('report', 'SoftLayer.CLI.report'),
     ('report:bandwidth', 'SoftLayer.CLI.report.bandwidth:cli'),
     ('report:datacenter-closures', 'SoftLayer.CLI.report.dc_closures:cli'),
+
+    ('autoscale', 'SoftLayer.CLI.autoscale'),
+    ('autoscale:list', 'SoftLayer.CLI.autoscale.list:cli'),
+    ('autoscale:detail', 'SoftLayer.CLI.autoscale.detail:cli'),
+    ('autoscale:delete', 'SoftLayer.CLI.autoscale.delete:cli'),
 ]
 
 ALL_ALIASES = {
diff --git a/SoftLayer/fixtures/SoftLayer_Network_Storage.py b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
index 409f1fd8..0854e6ba 100644
--- a/SoftLayer/fixtures/SoftLayer_Network_Storage.py
+++ b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
@@ -234,11 +234,21 @@
 enableSnapshots = True
 disableSnapshots = True
 
-getVolumeCountLimits = {
-    'datacenterName': 'global',
-    'maximumAvailableCount': 300,
-    'provisionedCount': 100
-}
+getVolumeCountLimits = [{
+    "datacenterName": "global",
+    "maximumAvailableCount": 700,
+    "provisionedCount": 2632
+},
+    {
+        "maximumAvailableCount": 50,
+        "provisionedCount": 2632
+    },
+    {
+        "datacenterName": "dal13",
+        "maximumAvailableCount": 52,
+        "provisionedCount": 30
+    },
+]
 
 refreshDuplicate = {
     'dependentDuplicate': 1
diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py b/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py
index bc01a68a..937bd93c 100644
--- a/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py
+++ b/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py
@@ -53,5 +53,4 @@
     {'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2},
     {'id': 814994, 'longName': 'Amsterdam 3', 'name': 'ams03', 'statusId': 2},
 ]
-permitSharingAccess = True
 denySharingAccess = True
diff --git a/SoftLayer/managers/autoscale.py b/SoftLayer/managers/autoscale.py
new file mode 100644
index 00000000..4a2716c8
--- /dev/null
+++ b/SoftLayer/managers/autoscale.py
@@ -0,0 +1,139 @@
+"""
+    SoftLayer.autoscale
+    ~~~~~~~~~~~~~~~~~~~
+    Autoscale manager
+
+    :license: MIT, see LICENSE for more details.
+"""
+
+
+class AutoScaleManager(object):
+    """Manager for interacting with Autoscale instances."""
+
+    def __init__(self, client):
+        self.client = client
+
+    def list(self, mask=None):
+        """Calls `SoftLayer_Account::getScaleGroups()`_
+
+        :param mask: optional SoftLayer_Scale_Group objectMask
+
+        .. _SoftLayer_Account::getScaleGroups():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Account/getScaleGroups/
+        """
+        if not mask:
+            mask = "mask[status,virtualGuestMemberCount]"
+
+        return self.client.call('SoftLayer_Account', 'getScaleGroups', mask=mask, iter=True)
+
+    def details(self, identifier, mask=None):
+        """Calls `SoftLayer_Scale_Group::getObject()`_
+
+        :param identifier: SoftLayer_Scale_Group id
+        :param mask: optional SoftLayer_Scale_Group objectMask
+
+        .. _SoftLayer_Scale_Group::getObject():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/getObject/
+        """
+        if not mask:
+            mask = """mask[virtualGuestMembers[id,virtualGuest[id,hostname,domain,provisionDate]], terminationPolicy,
+                   virtualGuestMemberCount, virtualGuestMemberTemplate[sshKeys],
+                   policies[id,name,createDate,cooldown,actions,triggers,scaleActions],
+                   networkVlans[networkVlanId,networkVlan[networkSpace,primaryRouter[hostname]]],
+                   loadBalancers, regionalGroup[locations]]"""
+        return self.client.call('SoftLayer_Scale_Group', 'getObject', id=identifier, mask=mask)
+
+    def get_policy(self, identifier, mask=None):
+        """Calls `SoftLayer_Scale_Policy::getObject()`_
+
+        :param identifier: SoftLayer_Scale_Policy id
+        :param mask: optional SoftLayer_Scale_Policy objectMask
+
+        .. _SoftLayer_Scale_Policy::getObject():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Policy/getObject/
+        """
+        if not mask:
+            mask = """mask[cooldown, createDate, id, name, actions, triggers[type]
+
+            ]"""
+
+        return self.client.call('SoftLayer_Scale_Policy', 'getObject', id=identifier, mask=mask)
+
+    def scale(self, identifier, amount):
+        """Calls `SoftLayer_Scale_Group::scale()`_
+
+        :param identifier: SoftLayer_Scale_Group Id
+        :param amount: positive or negative number to scale the group by
+
+        .. _SoftLayer_Scale_Group::scale():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/scale/
+        """
+        return self.client.call('SoftLayer_Scale_Group', 'scale', amount, id=identifier)
+
+    def scale_to(self, identifier, amount):
+        """Calls `SoftLayer_Scale_Group::scaleTo()`_
+
+        :param identifier: SoftLayer_Scale_Group Id
+        :param amount: number to scale the group to.
+
+        .. _SoftLayer_Scale_Group::scaleTo():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/scaleTo/
+        """
+        return self.client.call('SoftLayer_Scale_Group', 'scaleTo', amount, id=identifier)
+
+    def get_logs(self, identifier, mask=None, object_filter=None):
+        """Calls `SoftLayer_Scale_Group::getLogs()`_
+
+        :param identifier: SoftLayer_Scale_Group Id
+        :param mask: optional SoftLayer_Scale_Group_Log objectMask
+        :param object_filter: optional SoftLayer_Scale_Group_Log objectFilter
+
+        .. _SoftLayer_Scale_Group::getLogs():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/getLogs/
+        """
+        return self.client.call('SoftLayer_Scale_Group', 'getLogs', id=identifier, mask=mask, filter=object_filter,
+                                iter=True)
+
+    def get_virtual_guests(self, identifier, mask=None):
+        """Calls `SoftLayer_Scale_Group::getVirtualGuestMembers()`_
+
+        :param identifier: SoftLayer_Scale_Group Id
+        :param mask: optional SoftLayer_Scale_Member objectMask
+
+        .. _SoftLayer_Scale_Group::getVirtualGuestMembers():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/getVirtualGuestMembers/
+        """
+        return self.client.call('SoftLayer_Scale_Group', 'getVirtualGuestMembers', id=identifier, mask=mask, iter=True)
+
+    def edit(self, identifier, template):
+        """Calls `SoftLayer_Scale_Group::editObject()`_
+
+        :param identifier: SoftLayer_Scale_Group id
+        :param template: `SoftLayer_Scale_Group`_
+
+        .. _SoftLayer_Scale_Group::editObject():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/editObject/
+        .. _SoftLayer_Scale_Group: https://sldn.softlayer.com/reference/datatypes/SoftLayer_Scale_Group/
+        """
+        return self.client.call('SoftLayer_Scale_Group', 'editObject', template, id=identifier)
+
+    def create(self, template):
+        """Calls `SoftLayer_Scale_Group::createObject()`_
+
+               :param template: `SoftLayer_Scale_Group`_
+
+                .. _SoftLayer_Scale_Group::createObject():
+                    https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/createObject/
+                .. _SoftLayer_Scale_Group: https://sldn.softlayer.com/reference/datatypes/SoftLayer_Scale_Group/
+                """
+        return self.client.call('SoftLayer_Scale_Group', 'createObject', template)
+
+    def delete(self, identifier):
+        """Calls `SoftLayer_Scale_Group::forceDeleteObject()`_
+
+        :param identifier: SoftLayer_Scale_Group id
+
+        .. _SoftLayer_Scale_Group::forceDeleteObject():
+            https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/forceDeleteObject/
+        """
+        return self.client.call('SoftLayer_Scale_Group', 'forceDeleteObject', id=identifier)
diff --git a/SoftLayer/managers/image.py b/SoftLayer/managers/image.py
index 26043a8a..6c1fb3a9 100644
--- a/SoftLayer/managers/image.py
+++ b/SoftLayer/managers/image.py
@@ -238,15 +238,6 @@ def get_locations_list(self, image_id, location_names):
 
         return locations_ids
 
-    def share_image(self, image_id, account_id):
-        """Permit sharing image template with another account.
-
-        :param int image_id: The ID of the image.
-        :param int account_id: The ID of the another account to share the image.
-        """
-
-        return self.vgbdtg.permitSharingAccess(account_id, id=image_id)
-
     def deny_share_image(self, image_id, account_id):
         """Deny sharing image template with another account.
 
diff --git a/docs/cli/autoscale.rst b/docs/cli/autoscale.rst
new file mode 100644
index 00000000..61a44df1
--- /dev/null
+++ b/docs/cli/autoscale.rst
@@ -0,0 +1,46 @@
+.. _cli_autoscale:
+
+Autoscale Commands
+==================
+These commands were added in version `5.8.1 <https://github.com/softlayer/softlayer-python/releases/tag/v5.8.1>`_
+
+For making changes to the triggers or the autoscale group itself, see the `Autoscale Portal`_
+
+- `Autoscale Product <https://www.ibm.com/cloud/auto-scaling>`_
+- `Autoscale Documentation <https://cloud.ibm.com/docs/vsi?topic=virtual-servers-about-auto-scale>`_
+- `Autoscale Portal`_
+
+.. click:: SoftLayer.CLI.autoscale.list:cli
+    :prog: autoscale list
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.detail:cli
+    :prog: autoscale detail
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.scale:cli
+    :prog: autoscale scale
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.logs:cli
+    :prog: autoscale logs
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.tag:cli
+    :prog: autoscale tag
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.edit:cli
+    :prog: autoscale edit
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.delete:cli
+    :prog: autoscale delete
+    :show-nested:
+
+.. click:: SoftLayer.CLI.autoscale.create:cli
+    :prog: autoscale create
+    :show-nested:
+
+
+.. _Autoscale Portal: https://cloud.ibm.com/classic/autoscale
\ No newline at end of file
diff --git a/docs/cli/image.rst b/docs/cli/image.rst
index 9d36e98b..dc4a24d7 100644
--- a/docs/cli/image.rst
+++ b/docs/cli/image.rst
@@ -31,10 +31,6 @@ Disk Image Commands
     :prog: image datacenter
     :show-nested:
 
-.. click:: SoftLayer.CLI.image.share:cli
-    :prog: image share
-    :show-nested:
-
 .. click:: SoftLayer.CLI.image.share_deny:cli
     :prog: image share-deny
-    :show-nested:
+    :show-nested:
\ No newline at end of file


Test patch:
diff --git a/tests/CLI/modules/autoscale_tests.py b/tests/CLI/modules/autoscale_tests.py
new file mode 100644
index 00000000..2628d503
--- /dev/null
+++ b/tests/CLI/modules/autoscale_tests.py
@@ -0,0 +1,26 @@
+"""
+    SoftLayer.tests.CLI.modules.autoscale_tests
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :license: MIT, see LICENSE for more details.
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Tests for the autoscale cli command
+"""
+
+from SoftLayer import testing
+
+
+class AutoscaleTests(testing.TestCase):
+
+    def test_autoscale_list(self):
+        result = self.run_command(['autoscale', 'list'])
+        self.assert_no_fail(result)
+
+    def test_autoscale_detail(self):
+        result = self.run_command(['autoscale', 'detail', '12222222'])
+        self.assert_no_fail(result)
+
+    def test_autoscale_delete(self):
+        result = self.run_command(['autoscale', 'delete', '12345'])
+        self.assert_no_fail(result)
diff --git a/tests/CLI/modules/block_tests.py b/tests/CLI/modules/block_tests.py
index 3dfe42f7..27a20c92 100644
--- a/tests/CLI/modules/block_tests.py
+++ b/tests/CLI/modules/block_tests.py
@@ -779,6 +779,24 @@ def test_volume_limit(self, list_mock):
         result = self.run_command(['block', 'volume-limits'])
         self.assert_no_fail(result)
 
+    def test_volume_limit_empty_datacenter(self):
+        expect_result = {
+            'dal13': 52,
+            'global': 700,
+            'null': 50
+        }
+        result = self.run_command(['block', 'volume-limits'])
+        self.assert_no_fail(result)
+        self.assertEqual(json.loads(result.output), expect_result)
+
+    def test_volume_limit_datacenter(self):
+        expect_result = {
+            "dal13": 52
+        }
+        result = self.run_command(['block', 'volume-limits', '-d', 'dal13'])
+        self.assert_no_fail(result)
+        self.assertEqual(json.loads(result.output), expect_result)
+
     def test_dupe_refresh(self):
         result = self.run_command(['block', 'volume-refresh', '102', '103'])
 
diff --git a/tests/CLI/modules/file_tests.py b/tests/CLI/modules/file_tests.py
index 6943399b..b8c7f00b 100644
--- a/tests/CLI/modules/file_tests.py
+++ b/tests/CLI/modules/file_tests.py
@@ -761,6 +761,24 @@ def test_volume_limit(self, list_mock):
         result = self.run_command(['file', 'volume-limits'])
         self.assert_no_fail(result)
 
+    def test_volume_limit_empty_datacenter(self):
+        expect_result = {
+            'dal13': 52,
+            'global': 700,
+            'null': 50
+        }
+        result = self.run_command(['file', 'volume-limits'])
+        self.assert_no_fail(result)
+        self.assertEqual(json.loads(result.output), expect_result)
+
+    def test_volume_limit_datacenter(self):
+        expect_result = {
+            "dal13": 52
+        }
+        result = self.run_command(['file', 'volume-limits', '-d', 'dal13'])
+        self.assert_no_fail(result)
+        self.assertEqual(json.loads(result.output), expect_result)
+
     def test_dupe_refresh(self):
         result = self.run_command(['file', 'volume-refresh', '102', '103'])
 
diff --git a/tests/CLI/modules/image_tests.py b/tests/CLI/modules/image_tests.py
index 230b6bcf..515cdd1b 100644
--- a/tests/CLI/modules/image_tests.py
+++ b/tests/CLI/modules/image_tests.py
@@ -54,21 +54,6 @@ def test_datacenter_remove_fails(self):
         result = self.run_command(['image', 'datacenter', '100', '--remove'])
         self.assertEqual(2, result.exit_code)
 
-    def test_share(self):
-        result = self.run_command(['image', 'share', '123456', '--account-id', '654321'])
-        self.assert_no_fail(result)
-        self.assertIn("Image template 123456 was shared to account 654321.", result.output)
-
-    def test_share_without_id(self):
-        result = self.run_command(['image', 'share'])
-        self.assertEqual(2, result.exit_code)
-        self.assertIn("Error: Missing argument 'IDENTIFIER'.", result.output)
-
-    def test_share_without_id_account(self):
-        result = self.run_command(['image', 'share', "123456"])
-        self.assertEqual(2, result.exit_code)
-        self.assertIn("Error: Missing option '--account-id'.", result.output)
-
     def test_deny_share(self):
         result = self.run_command(['image', 'share-deny', '123456', '--account-id', '654321'])
         self.assert_no_fail(result)
diff --git a/tests/managers/autoscale_tests.py b/tests/managers/autoscale_tests.py
new file mode 100644
index 00000000..e477bacd
--- /dev/null
+++ b/tests/managers/autoscale_tests.py
@@ -0,0 +1,180 @@
+"""
+    SoftLayer.tests.managers.autoscale_tests
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :license: MIT, see LICENSE for more details.
+"""
+
+from SoftLayer.managers.autoscale import AutoScaleManager
+from SoftLayer import testing
+
+
+class AutoScaleTests(testing.TestCase):
+
+    def set_up(self):
+        self.autoscale = AutoScaleManager(self.client)
+
+    def test_autoscale_list(self):
+        self.autoscale.list()
+
+        self.assert_called_with(
+            'SoftLayer_Account',
+            'getScaleGroups'
+        )
+
+    def test_autoscale_list_with_mask(self):
+        self.autoscale.list(mask='mask[status,virtualGuestMemberCount]')
+
+        self.assert_called_with(
+            'SoftLayer_Account',
+            'getScaleGroups'
+        )
+
+    def test_autoscale_details(self):
+        self.autoscale.details(11111)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'getObject',
+            identifier=11111
+        )
+
+    def test_autoscale_details_with_mask(self):
+        self.autoscale.details(11111, mask='mask[virtualGuestMembers[id,virtualGuest[hostname,domain,provisionDate]], '
+                                           'terminationPolicy,virtualGuestMemberCount]')
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'getObject',
+            identifier=11111
+        )
+
+    def test_autoscale_policy(self):
+        self.autoscale.get_policy(11111)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Policy',
+            'getObject',
+            identifier=11111
+        )
+
+    def test_autoscale_policy_with_mask(self):
+        self.autoscale.get_policy(11111, mask='mask[cooldown, createDate, id, name, actions, triggers[type]]')
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Policy',
+            'getObject',
+            identifier=11111
+        )
+
+    def test_autoscale_scale(self):
+        self.autoscale.scale(11111, 3)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'scale',
+            identifier=11111
+        )
+
+    def test_autoscale_scaleTo(self):
+        self.autoscale.scale_to(11111, 3)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'scaleTo',
+            identifier=11111
+        )
+
+    def test_autoscale_getLogs(self):
+        self.autoscale.get_logs(11111)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'getLogs',
+            identifier=11111
+        )
+
+    def test_autoscale_get_virtual_guests(self):
+        self.autoscale.get_virtual_guests(11111)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'getVirtualGuestMembers',
+            identifier=11111,
+            mask=None
+        )
+
+    def test_autoscale_get_virtual_guests_mask(self):
+        test_mask = "mask[id]"
+        self.autoscale.get_virtual_guests(11111, mask=test_mask)
+
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'getVirtualGuestMembers',
+            identifier=11111,
+            mask=test_mask
+        )
+
+    def test_edit_object(self):
+        template = {'name': 'test'}
+        self.autoscale.edit(12345, template)
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'editObject',
+            args=(template,),
+            identifier=12345)
+
+    def test_create_object(self):
+        template = {
+            'name': 'test',
+            'cooldown': 3600,
+            'maximumMemberCount': 5,
+            'minimumMemberCount': 1,
+            'regionalGroupId': 4568,
+            'suspendedFlag': False,
+            'balancedTerminationFlag': False,
+            'virtualGuestMemberTemplate': {
+                'domain': 'test.com',
+                'hostname': 'testvs',
+                'operatingSystemReferenceCode': 'CENTOS_7_64',
+                'maxMemory': 2048,
+                'datacenter': {
+                    'id': 265592
+                },
+                'startCpus': 2,
+                'blockDevices': [
+                    {
+                        'diskImage': {
+                            'capacity': '100'
+                        },
+                        'device': 0
+                    }
+                ],
+                'hourlyBillingFlag': True,
+                'networkComponents': [
+                    {
+                        'maxSpeed': 100
+                    }
+                ],
+                'localDiskFlag': True,
+                'typeId': 1,
+                'userData': [
+                    {
+                        'value': 'the userData'
+                    }
+                ]
+            },
+            'virtualGuestMemberCount': 0,
+
+            'terminationPolicyId': '2',
+        }
+
+        self.autoscale.create(template)
+        self.assert_called_with(
+            'SoftLayer_Scale_Group',
+            'createObject',
+            args=(template,))
+
+    def test_delete_object(self):
+        self.autoscale.delete(12345)
+        self.assert_called_with('SoftLayer_Scale_Group', 'forceDeleteObject')
diff --git a/tests/managers/image_tests.py b/tests/managers/image_tests.py
index debe1700..31d7385d 100644
--- a/tests/managers/image_tests.py
+++ b/tests/managers/image_tests.py
@@ -224,12 +224,6 @@ def test_get_locations_id_fails(self):
             locations
         )
 
-    def test_share_image(self):
-        result = self.image.share_image(image_id=123456, account_id=654321)
-
-        self.assert_called_with(IMAGE_SERVICE, 'permitSharingAccess', identifier=123456, args=(654321,))
-        self.assertEqual(True, result)
-
     def test_deny_share_image(self):
         result = self.image.deny_share_image(image_id=123456, account_id=654321)