Repository: softlayer/softlayer-python
Pull request #1245 (2020-03-20 20:02:11)

Issue1234


Patch:
diff --git a/SoftLayer/CLI/call_api.py b/SoftLayer/CLI/call_api.py
index cbce4ecc..6e16a2a7 100644
--- a/SoftLayer/CLI/call_api.py
+++ b/SoftLayer/CLI/call_api.py
@@ -1,6 +1,4 @@
 """Call arbitrary API endpoints."""
-import json
-
 import click
 
 from SoftLayer.CLI import environment
@@ -29,7 +27,8 @@ def _build_filters(_filters):
             if len(top_parts) == 2:
                 break
         else:
-            raise exceptions.CLIAbort('Failed to find valid operation for: %s' % _filter)
+            raise exceptions.CLIAbort('Failed to find valid operation for: %s'
+                                      % _filter)
 
         key, value = top_parts
         current = root
@@ -69,61 +68,25 @@ def _build_python_example(args, kwargs):
     return call_str
 
 
-def _validate_filter(ctx, param, value):  # pylint: disable=unused-argument
-    """Validates a JSON style object filter"""
-    _filter = None
-    if value:
-        try:
-            _filter = json.loads(value)
-            if not isinstance(_filter, dict):
-                raise exceptions.CLIAbort("\"{}\" should be a JSON object, but is a {} instead.".
-                                          format(_filter, type(_filter)))
-        except json.JSONDecodeError as error:
-            raise exceptions.CLIAbort("\"{}\" is not valid JSON. {}".format(value, error))
-
-    return _filter
-
-
-def _validate_parameters(ctx, param, value):  # pylint: disable=unused-argument
-    """Checks if value is a JSON string, and converts it to a datastructure if that is true"""
-
-    validated_values = []
-    for parameter in value:
-        if isinstance(parameter, str):
-            # looks like a JSON string...
-            if '{' in parameter or '[' in parameter:
-                try:
-                    parameter = json.loads(parameter)
-                except json.JSONDecodeError as error:
-                    click.secho("{} looked like json, but was invalid, passing to API as is. {}".
-                                format(parameter, error), fg='red')
-        validated_values.append(parameter)
-    return validated_values
-
-
 @click.command('call', short_help="Call arbitrary API endpoints.")
 @click.argument('service')
 @click.argument('method')
-@click.argument('parameters', nargs=-1, callback=_validate_parameters)
+@click.argument('parameters', nargs=-1)
 @click.option('--id', '_id', help="Init parameter")
 @helpers.multi_option('--filter', '-f', '_filters',
-                      help="Object filters. This should be of the form: 'property=value' or 'nested.property=value'."
-                           "Complex filters should use --json-filter.")
+                      help="Object filters. This should be of the form: "
+                      "'property=value' or 'nested.property=value'. Complex "
+                      "filters like betweenDate are not currently supported.")
 @click.option('--mask', help="String-based object mask")
 @click.option('--limit', type=click.INT, help="Result limit")
 @click.option('--offset', type=click.INT, help="Result offset")
 @click.option('--output-python / --no-output-python',
               help="Show python example code instead of executing the call")
-@click.option('--json-filter', callback=_validate_filter,
-              help="A JSON string to be passed in as the object filter to the API call. "
-                   "Remember to use double quotes (\") for variable names. Can NOT be used with --filter. "
-                   "Dont use whitespace outside of strings, or the slcli might have trouble parsing it.")
 @environment.pass_env
 def cli(env, service, method, parameters, _id, _filters, mask, limit, offset,
-        output_python=False, json_filter=None):
+        output_python=False):
     """Call arbitrary API endpoints with the given SERVICE and METHOD.
 
-    For parameters that require a datatype, use a JSON string for that parameter.
     Example::
 
         slcli call-api Account getObject
@@ -137,23 +100,12 @@ def cli(env, service, method, parameters, _id, _filters, mask, limit, offset,
             --mask=id,hostname,datacenter.name,maxCpu
         slcli call-api Account getVirtualGuests \\
             -f 'virtualGuests.datacenter.name IN dal05,sng01'
-        slcli call-api Account getVirtualGuests \\
-            --json-filter  '{"virtualGuests":{"hostname":{"operation":"^= test"}}}' --limit=10
-        slcli -v call-api SoftLayer_User_Customer addBulkPortalPermission --id=1234567 \\
-            '[{"keyName": "NETWORK_MESSAGE_DELIVERY_MANAGE"}]'
     """
 
-    if _filters and json_filter:
-        raise exceptions.CLIAbort("--filter and --json-filter cannot be used together.")
-
-    object_filter = _build_filters(_filters)
-    if json_filter:
-        object_filter.update(json_filter)
-
     args = [service, method] + list(parameters)
     kwargs = {
         'id': _id,
-        'filter': object_filter,
+        'filter': _build_filters(_filters),
         'mask': mask,
         'limit': limit,
         'offset': offset,
diff --git a/SoftLayer/CLI/deprecated.py b/SoftLayer/CLI/deprecated.py
new file mode 100644
index 00000000..0609a924
--- /dev/null
+++ b/SoftLayer/CLI/deprecated.py
@@ -0,0 +1,14 @@
+"""
+    SoftLayer.CLI.deprecated
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+    Handles usage of the deprecated command name, 'sl'.
+    :license: MIT, see LICENSE for more details.
+"""
+import sys
+
+
+def main():
+    """Main function for the deprecated 'sl' command."""
+    print("ERROR: Use the 'slcli' command instead.", file=sys.stderr)
+    print("> slcli %s" % ' '.join(sys.argv[1:]), file=sys.stderr)
+    sys.exit(-1)
diff --git a/SoftLayer/CLI/image/datacenter.py b/SoftLayer/CLI/image/datacenter.py
new file mode 100644
index 00000000..8d59c6db
--- /dev/null
+++ b/SoftLayer/CLI/image/datacenter.py
@@ -0,0 +1,28 @@
+"""Edit details of an image."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import helpers
+
+
+@click.command()
+@click.argument('identifier')
+@click.option('--add/--remove', default=True,
+              help="To add or remove Datacenter")
+@click.argument('locations', nargs=-1, required=True)
+@environment.pass_env
+def cli(env, identifier, add, locations):
+    """Add/Remove datacenter of an image."""
+
+    image_mgr = SoftLayer.ImageManager(env.client)
+    image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image')
+
+    if add:
+        result = image_mgr.add_locations(image_id, locations)
+    else:
+        result = image_mgr.remove_locations(image_id, locations)
+
+    env.fout(result)
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index b908be4f..f6edee47 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -163,6 +163,7 @@
     ('image:list', 'SoftLayer.CLI.image.list:cli'),
     ('image:import', 'SoftLayer.CLI.image.import:cli'),
     ('image:export', 'SoftLayer.CLI.image.export:cli'),
+    ('image:datacenter', 'SoftLayer.CLI.image.datacenter:cli'),
 
     ('ipsec', 'SoftLayer.CLI.vpn.ipsec'),
     ('ipsec:configure', 'SoftLayer.CLI.vpn.ipsec.configure:cli'),
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 785ed3b0..e2e7f0e4 100644
--- a/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py
+++ b/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py
@@ -7,6 +7,11 @@
     'name': 'test_image',
     'parentId': '',
     'publicFlag': True,
+    'children': [{
+        'datacenter': {
+            'name': 'ams01'
+        }
+    }],
 }, {
     'accountId': 1234,
     'blockDevices': [],
@@ -16,6 +21,11 @@
     'name': 'test_image2',
     'parentId': '',
     'publicFlag': True,
+    'children': [{
+        'datacenter': {
+            'name': 'ams01'
+        }
+    }],
 }]
 
 getObject = IMAGES[0]
@@ -23,17 +33,23 @@
 deleteObject = {}
 editObject = True
 setTags = True
-createFromExternalSource = [{
+createFromExternalSource = {
     'createDate': '2013-12-05T21:53:03-06:00',
     'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D',
     'id': 100,
     'name': 'test_image',
-}]
-createFromIcos = [{
+}
+createFromIcos = {
     'createDate': '2013-12-05T21:53:03-06:00',
     'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D',
     'id': 100,
     'name': 'test_image',
-}]
+}
 copyToExternalSource = True
 copyToIcos = True
+addLocations = True
+removeLocations = True
+getStorageLocations = [
+    {'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2},
+    {'id': 814994, 'longName': 'Amsterdam 3', 'name': 'ams03', 'statusId': 2},
+]
diff --git a/SoftLayer/managers/image.py b/SoftLayer/managers/image.py
index 34efb36b..d30b0530 100644
--- a/SoftLayer/managers/image.py
+++ b/SoftLayer/managers/image.py
@@ -6,6 +6,7 @@
     :license: MIT, see LICENSE for more details.
 """
 
+from SoftLayer import exceptions
 from SoftLayer import utils
 
 IMAGE_MASK = ('id,accountId,name,globalIdentifier,blockDevices,parentId,'
@@ -187,3 +188,52 @@ def export_image_to_uri(self, image_id, uri, ibm_api_key=None):
             }, id=image_id)
         else:
             return self.vgbdtg.copyToExternalSource({'uri': uri}, id=image_id)
+
+    def add_locations(self, image_id, location_names):
+        """Add available locations to an archive image template.
+
+        :param int image_id: The ID of the image
+        :param location_names: Locations for the Image.
+        """
+        locations = self.get_locations_list(image_id, location_names)
+        return self.vgbdtg.addLocations(locations, id=image_id)
+
+    def remove_locations(self, image_id, location_names):
+        """Remove available locations from an archive image template.
+
+        :param int image_id: The ID of the image
+        :param location_names: Locations for the Image.
+        """
+        locations = self.get_locations_list(image_id, location_names)
+        return self.vgbdtg.removeLocations(locations, id=image_id)
+
+    def get_storage_locations(self, image_id):
+        """Get available locations for public image storage.
+
+        :param int image_id: The ID of the image
+        """
+        return self.vgbdtg.getStorageLocations(id=image_id)
+
+    def get_locations_list(self, image_id, location_names):
+        """Converts a list of location names to a list of locations.
+
+        :param int image_id: The ID of the image.
+        :param list location_names: A list of location names strings.
+        :returns: A list of locations associated with the given location names in the image.
+        """
+        locations = self.get_storage_locations(image_id)
+        locations_ids = []
+        matching_location = {}
+        output_error = "Location {} does not exist for available locations for image {}"
+
+        for location_name in location_names:
+            for location in locations:
+                if location_name == location.get('name'):
+                    matching_location = location
+                    break
+            if matching_location.get('id') is None:
+                raise exceptions.SoftLayerError(output_error.format(location_name, image_id))
+
+            locations_ids.append(matching_location)
+
+        return locations_ids
diff --git a/SoftLayer/managers/ordering.py b/SoftLayer/managers/ordering.py
index 7e4e81db..e86679f6 100644
--- a/SoftLayer/managers/ordering.py
+++ b/SoftLayer/managers/ordering.py
@@ -351,7 +351,7 @@ def get_price_id_list(self, package_keyname, item_keynames, core=None):
                   keynames in the given package
 
         """
-        mask = 'id, description, capacity, itemCategory, keyName, prices[categories]'
+        mask = 'id, capacity, itemCategory, keyName, prices[categories]'
         items = self.list_items(package_keyname, mask=mask)
         item_capacity = self.get_item_capacity(items, item_keynames)
 
@@ -422,11 +422,6 @@ def get_item_capacity(self, items, item_keynames):
                     if "TIER" in item["keyName"]:
                         item_capacity = item['capacity']
                         break
-                    if "INTEL" in item["keyName"]:
-                        item_split = item['description'].split("(")
-                        item_core = item_split[1].split(" ")
-                        item_capacity = item_core[0]
-                        break
         return item_capacity
 
     def get_preset_prices(self, preset):
diff --git a/docs/cli/commands.rst b/docs/cli/commands.rst
index c1c5bd3f..a577adcd 100644
--- a/docs/cli/commands.rst
+++ b/docs/cli/commands.rst
@@ -3,14 +3,6 @@
 Call API
 ========
 
-This function allows you to easily call any API. The format is
-
-`slcli call-api SoftLayer_Service method param1 param2 --id=1234 --mask="mask[id,name]"`
-
-Parameters should be in the order they are presented on sldn.softlayer.com. 
-Any complex parameters (those that link to other datatypes) should be presented as JSON strings. They need to be enclosed in single quotes (`'`), and variables and strings enclosed in double quotes (`"`).
-
-For example: `{"hostname":"test",ssh_keys:[{"id":1234}]}`
 
 .. click:: SoftLayer.CLI.call_api:cli
     :prog: call-api


Test patch:
diff --git a/SoftLayer/testing/__init__.py b/SoftLayer/testing/__init__.py
index 9c8b81c4..d02e60f0 100644
--- a/SoftLayer/testing/__init__.py
+++ b/SoftLayer/testing/__init__.py
@@ -8,10 +8,10 @@
 # pylint: disable=invalid-name
 import logging
 import os.path
-import unittest
 
 from click import testing
 import mock
+import testtools
 
 import SoftLayer
 from SoftLayer.CLI import core
@@ -68,7 +68,8 @@ def _record_call(self, call):
                      'offset']:
             details.append('%s=%r' % (prop, getattr(call, prop)))
 
-        logging.info('%s::%s called; %s', call.service, call.method, '; '.join(details))
+        logging.info('%s::%s called; %s',
+                     call.service, call.method, '; '.join(details))
 
 
 def _mock_key(service, method):
@@ -76,7 +77,7 @@ def _mock_key(service, method):
     return '%s::%s' % (service, method)
 
 
-class TestCase(unittest.TestCase):
+class TestCase(testtools.TestCase):
     """Testcase class with PEP-8 compatible method names."""
 
     @classmethod
@@ -99,7 +100,7 @@ def tear_down(self):
         """Aliased from tearDown."""
 
     def setUp(self):  # NOQA
-        unittest.TestCase.setUp(self)
+        testtools.TestCase.setUp(self)
 
         self.mocks.clear()
 
@@ -113,7 +114,7 @@ def setUp(self):  # NOQA
         self.set_up()
 
     def tearDown(self):  # NOQA
-        super(TestCase, self).tearDown()
+        testtools.TestCase.tearDown(self)
         self.tear_down()
         self.mocks.clear()
 
@@ -140,7 +141,8 @@ def assert_called_with(self, service, method, **props):
         if self.calls(service, method, **props):
             return
 
-        raise AssertionError('%s::%s was not called with given properties: %s' % (service, method, props))
+        raise AssertionError('%s::%s was not called with given properties: %s'
+                             % (service, method, props))
 
     def assert_no_fail(self, result):
         """Fail when a failing click result has an error"""
@@ -168,17 +170,6 @@ def run_command(self, args=None, env=None, fixtures=True, fmt='json', stdin=None
         runner = testing.CliRunner()
         return runner.invoke(core.cli, args=args, input=stdin, obj=env or self.env)
 
-    def assertRaises(self, exception, function_callable, *args, **kwds):  # pylint: disable=arguments-differ
-        """Converts testtools.assertRaises to unittest.assertRaises calls.
-
-        testtools==2.4.0 require unittest2, which breaks pytest>=5.4.1 on skipTest.
-        But switching to just using unittest breaks assertRaises because the format is slightly different.
-        This basically just reformats the call so I don't have to re-write a bunch of tests.
-        """
-        with super(TestCase, self).assertRaises(exception) as cm:
-            function_callable(*args, **kwds)
-        return cm.exception
-
 
 def call_has_props(call, props):
     """Check if a call has matching properties of a given props dictionary."""
diff --git a/tests/CLI/deprecated_tests.py b/tests/CLI/deprecated_tests.py
new file mode 100644
index 00000000..f28025f3
--- /dev/null
+++ b/tests/CLI/deprecated_tests.py
@@ -0,0 +1,33 @@
+"""
+    SoftLayer.tests.CLI.deprecated_tests
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :license: MIT, see LICENSE for more details.
+"""
+import io
+
+import mock
+
+from SoftLayer.CLI import deprecated
+from SoftLayer import testing
+
+
+class EnvironmentTests(testing.TestCase):
+
+    def test_main(self):
+
+        with mock.patch('sys.stderr', new=io.StringIO()) as fake_out:
+            ex = self.assertRaises(SystemExit, deprecated.main)
+            self.assertEqual(ex.code, -1)
+
+            self.assertIn("ERROR: Use the 'slcli' command instead.",
+                          fake_out.getvalue())
+
+    def test_with_args(self):
+        with mock.patch('sys.stderr', new=io.StringIO()) as fake_out:
+            with mock.patch('sys.argv', new=['sl', 'module', 'subcommand']):
+                ex = self.assertRaises(SystemExit, deprecated.main)
+                self.assertEqual(ex.code, -1)
+
+                self.assertIn("ERROR: Use the 'slcli' command instead.",
+                              fake_out.getvalue())
diff --git a/tests/CLI/modules/call_api_tests.py b/tests/CLI/modules/call_api_tests.py
index 8d3f19ab..d99c59d3 100644
--- a/tests/CLI/modules/call_api_tests.py
+++ b/tests/CLI/modules/call_api_tests.py
@@ -158,7 +158,8 @@ def test_object_table(self):
                              'None': None,
                              'Bool': True}
 
-        result = self.run_command(['call-api', 'Service', 'method'], fmt='table')
+        result = self.run_command(['call-api', 'Service', 'method'],
+                                  fmt='table')
 
         self.assert_no_fail(result)
         # NOTE(kmcdonald): Order is not guaranteed
@@ -178,7 +179,8 @@ def test_object_nested(self):
         result = self.run_command(['call-api', 'Service', 'method'])
 
         self.assert_no_fail(result)
-        self.assertEqual(json.loads(result.output),  {'this': {'is': [{'pretty': 'nested'}]}})
+        self.assertEqual(json.loads(result.output),
+                         {'this': {'is': [{'pretty': 'nested'}]}})
 
     def test_list(self):
         mock = self.set_mock('SoftLayer_Service', 'method')
@@ -206,7 +208,8 @@ def test_list_table(self):
                               'None': None,
                               'Bool': True}]
 
-        result = self.run_command(['call-api', 'Service', 'method'], fmt='table')
+        result = self.run_command(['call-api', 'Service', 'method'],
+                                  fmt='table')
 
         self.assert_no_fail(result)
         self.assertEqual(result.output,
@@ -221,10 +224,12 @@ def test_parameters(self):
         mock = self.set_mock('SoftLayer_Service', 'method')
         mock.return_value = {}
 
-        result = self.run_command(['call-api', 'Service', 'method', 'arg1', '1234'])
+        result = self.run_command(['call-api', 'Service', 'method',
+                                   'arg1', '1234'])
 
         self.assert_no_fail(result)
-        self.assert_called_with('SoftLayer_Service', 'method', args=('arg1', '1234'))
+        self.assert_called_with('SoftLayer_Service', 'method',
+                                args=('arg1', '1234'))
 
     def test_fixture_not_implemented(self):
         service = 'SoftLayer_Test'
@@ -259,42 +264,3 @@ def test_fixture_exception(self):
         self.assertIsInstance(result.exception, SoftLayerAPIError)
         output = '%s::%s fixture is not implemented' % (call_service, call_method)
         self.assertIn(output, result.exception.faultString)
-
-    def test_json_filter_validation(self):
-        json_filter = '{"test":"something"}'
-        result = call_api._validate_filter(None, None, json_filter)
-        self.assertEqual(result['test'], 'something')
-
-        # Valid JSON, but we expect objects, not simple types
-        with pytest.raises(exceptions.CLIAbort):
-            call_api._validate_filter(None, None, '"test"')
-
-        # Invalid JSON
-        with pytest.raises(exceptions.CLIAbort):
-            call_api._validate_filter(None, None, 'test')
-
-        # Empty Request
-        result = call_api._validate_filter(None, None, None)
-        self.assertEqual(None, result)
-
-    def test_json_parameters_validation(self):
-        json_params = ('{"test":"something"}', 'String', 1234, '[{"a":"b"}]', '{funky non [ Json')
-        result = call_api._validate_parameters(None, None, json_params)
-        self.assertEqual(result[0], {"test": "something"})
-        self.assertEqual(result[1], "String")
-        self.assertEqual(result[2], 1234)
-        self.assertEqual(result[3], [{"a": "b"}])
-        self.assertEqual(result[4], "{funky non [ Json")
-
-    def test_filter_with_filter(self):
-        result = self.run_command(['call-api', 'Account', 'getObject', '--filter=nested.property=5432',
-                                   '--json-filter={"test":"something"}'])
-        self.assertEqual(2, result.exit_code)
-        self.assertEqual(result.exception.message, "--filter and --json-filter cannot be used together.")
-        self.assertIsInstance(result.exception, exceptions.CLIAbort)
-
-    def test_json_filter(self):
-        pass
-        result = self.run_command(['call-api', 'Account', 'getObject', '--json-filter={"test":"something"}'])
-        self.assert_no_fail(result)
-        self.assert_called_with('SoftLayer_Account', 'getObject', filter={"test": "something"})
diff --git a/tests/CLI/modules/image_tests.py b/tests/CLI/modules/image_tests.py
new file mode 100644
index 00000000..9305cc4b
--- /dev/null
+++ b/tests/CLI/modules/image_tests.py
@@ -0,0 +1,55 @@
+"""
+    SoftLayer.tests.CLI.modules.image_tests
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :license: MIT, see LICENSE for more details.
+"""
+
+from SoftLayer import testing
+
+
+class ImageTests(testing.TestCase):
+
+    def test_detail(self):
+        result = self.run_command(['image', 'detail', '100'])
+        self.assert_no_fail(result)
+
+    def test_delete(self):
+        result = self.run_command(['image', 'delete', '100'])
+        self.assert_no_fail(result)
+
+    def test_edit_note(self):
+        result = self.run_command(['image', 'edit', '100', '--note=test'])
+        self.assert_no_fail(result)
+
+    def test_edit_name(self):
+        result = self.run_command(['image', 'edit', '100', '--name=test'])
+        self.assert_no_fail(result)
+
+    def test_edit_tag(self):
+        result = self.run_command(['image', 'edit', '100', '--tag=test'])
+        self.assert_no_fail(result)
+
+    def test_import(self):
+        result = self.run_command(['image', 'import', '100', 'swift://test'])
+        self.assert_no_fail(result)
+
+    def test_export(self):
+        result = self.run_command(['image', 'export', '100', 'swift://test'])
+        self.assert_no_fail(result)
+
+    def test_list(self):
+        result = self.run_command(['image', 'list'])
+        self.assert_no_fail(result)
+
+    def test_datacenter_add(self):
+        result = self.run_command(['image', 'datacenter', '100', '--add', 'ams01'])
+        self.assert_no_fail(result)
+
+    def test_datacenter_remove(self):
+        result = self.run_command(['image', 'datacenter', '100', '--remove', 'ams01'])
+        self.assert_no_fail(result)
+
+    def test_datacenter_remove_fails(self):
+        result = self.run_command(['image', 'datacenter', '100', '--remove'])
+        self.assertEqual(2, result.exit_code)
diff --git a/tests/managers/image_tests.py b/tests/managers/image_tests.py
index b36deea7..e8d585ac 100644
--- a/tests/managers/image_tests.py
+++ b/tests/managers/image_tests.py
@@ -6,6 +6,7 @@
 """
 
 import SoftLayer
+from SoftLayer import exceptions
 from SoftLayer import testing
 
 IMAGE_SERVICE = 'SoftLayer_Virtual_Guest_Block_Device_Template_Group'
@@ -192,3 +193,33 @@ def test_export_image_cos(self):
             'copyToIcos',
             args=({'uri': 'cos://someuri', 'ibmApiKey': 'someApiKey'},),
             identifier=1234)
+
+    def test_add_locations_image(self):
+        locations = ['ams01']
+        self.image.add_locations(100, locations)
+
+        self.assert_called_with(IMAGE_SERVICE, 'addLocations', identifier=100)
+
+    def test_add_locations_fail(self):
+        locations = ['test']
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.image.add_locations,
+            100,
+            locations
+        )
+
+    def test_remove_locations_image(self):
+        locations = ['ams01']
+        self.image.remove_locations(100, locations)
+
+        self.assert_called_with(IMAGE_SERVICE, 'removeLocations', identifier=100)
+
+    def test_get_locations_id_fails(self):
+        locations = ['test']
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.image.get_locations_list,
+            100,
+            locations
+        )
diff --git a/tests/managers/ordering_tests.py b/tests/managers/ordering_tests.py
index 39caa72f..045517cd 100644
--- a/tests/managers/ordering_tests.py
+++ b/tests/managers/ordering_tests.py
@@ -309,7 +309,7 @@ def test_get_price_id_list(self):
 
             prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM2'], "8")
 
-        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, '
+        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, capacity, itemCategory, keyName, '
                                                                   'prices[categories]')
         self.assertEqual([price1['id'], price2['id']], prices)
 
@@ -327,7 +327,7 @@ def test_get_price_id_list_no_core(self):
 
             prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM2'], None)
 
-        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, '
+        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, capacity, itemCategory, keyName, '
                                                                   'prices[categories]')
         self.assertEqual([price1['id'], price2['id']], prices)
 
@@ -342,7 +342,7 @@ def test_get_price_id_list_item_not_found(self):
             exc = self.assertRaises(exceptions.SoftLayerError,
                                     self.ordering.get_price_id_list,
                                     'PACKAGE_KEYNAME', ['ITEM2'], "8")
-        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, '
+        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, capacity, itemCategory, keyName, '
                                                                   'prices[categories]')
         self.assertEqual("Item ITEM2 does not exist for package PACKAGE_KEYNAME", str(exc))
 
@@ -357,8 +357,8 @@ def test_get_price_id_list_gpu_items_with_two_categories(self):
 
             prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM1'], "8")
 
-            list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, '
-                                                                      'keyName, ' 'prices[categories]')
+            list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, capacity, itemCategory, keyName, '
+                                                                      'prices[categories]')
             self.assertEqual([price2['id'], price1['id']], prices)
 
     def test_generate_no_complex_type(self):
@@ -608,7 +608,7 @@ def test_location_group_id_none(self):
 
             prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM2'], "8")
 
-        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, '
+        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, capacity, itemCategory, keyName, '
                                                                   'prices[categories]')
         self.assertEqual([price1['id'], price2['id']], prices)
 
@@ -626,7 +626,7 @@ def test_location_groud_id_empty(self):
 
             prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM2'], "8")
 
-        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, '
+        list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, capacity, itemCategory, keyName, '
                                                                   'prices[categories]')
         self.assertEqual([price1['id'], price2['id']], prices)
 
@@ -673,10 +673,11 @@ def test_issues1067(self):
             {
                 'id': 10453,
                 'itemCategory': {'categoryCode': 'server'},
-                "description": "Dual Intel Xeon Silver 4110 (16 Cores, 2.10 GHz)",
                 'keyName': 'INTEL_INTEL_XEON_4110_2_10',
                 'prices': [
                     {
+                        'capacityRestrictionMaximum': '2',
+                        'capacityRestrictionMinimum': '2',
                         'capacityRestrictionType': 'PROCESSOR',
                         'categories': [{'categoryCode': 'os'}],
                         'id': 201161,
@@ -743,21 +744,3 @@ def test_get_item_capacity_storage(self):
         item_capacity = self.ordering.get_item_capacity(items, ['READHEAVY_TIER', 'STORAGE_SPACE_FOR_2_IOPS_PER_GB'])
 
         self.assertEqual(1, int(item_capacity))
-
-    def test_get_item_capacity_intel(self):
-
-        items = [{
-            "capacity": "1",
-            "id": 6131,
-            "description": "Dual Intel Xeon E5-2690 v3 (24 Cores, 2.60 GHz)",
-            "keyName": "INTEL_XEON_2690_2_60",
-        },
-            {
-                "capacity": "1",
-                "id": 10201,
-                "keyName": "GUEST_CORE_1_DEDICATED",
-        }]
-
-        item_capacity = self.ordering.get_item_capacity(items, ['INTEL_XEON_2690_2_60', 'BANDWIDTH_20000_GB'])
-
-        self.assertEqual(24, int(item_capacity))