Repository: softlayer/softlayer-python
Pull request #886 (2017-10-13 18:28:28)

Issues885
addresses #885


Patch:
diff --git a/SoftLayer/CLI/hardware/power.py b/SoftLayer/CLI/hardware/power.py
index 22f32ade..64bba114 100644
--- a/SoftLayer/CLI/hardware/power.py
+++ b/SoftLayer/CLI/hardware/power.py
@@ -77,3 +77,20 @@ def power_cycle(env, identifier):
         raise exceptions.CLIAbort('Aborted.')
 
     env.client['Hardware_Server'].powerCycle(id=hw_id)
+
+
+@click.command()
+@click.argument('identifier')
+@environment.pass_env
+def rescue(env, identifier):
+    """Reboot server into a rescue image."""
+
+    mgr = SoftLayer.HardwareManager(env.client)
+    hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware')
+
+    if not (env.skip_confirmations or
+            formatting.confirm("This action will reboot this server. Continue?")):
+
+        raise exceptions.CLIAbort('Aborted')
+
+    env.client['Hardware_Server'].bootToRescueLayer(id=hw_id)
diff --git a/SoftLayer/CLI/hardware/rescue.py b/SoftLayer/CLI/hardware/rescue.py
deleted file mode 100644
index 6aaca3ef..00000000
--- a/SoftLayer/CLI/hardware/rescue.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""Reboot server into a rescue image."""
-# :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
-from SoftLayer.CLI import helpers
-
-
-@click.command()
-@click.argument('identifier')
-@environment.pass_env
-def cli(env, identifier):
-    """Reboot server into a rescue image."""
-
-    server = SoftLayer.HardwareManager(env.client)
-    server_id = helpers.resolve_id(server.resolve_ids, identifier, 'hardware')
-
-    if not (env.skip_confirmations or
-            formatting.confirm("This action will reboot this server. "
-                               "Continue?")):
-
-        raise exceptions.CLIAbort('Aborted')
-
-    server.rescue(server_id)
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index eae0c763..2ff3379a 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -210,6 +210,7 @@
     ('hardware:reload', 'SoftLayer.CLI.hardware.reload:cli'),
     ('hardware:credentials', 'SoftLayer.CLI.hardware.credentials:cli'),
     ('hardware:update-firmware', 'SoftLayer.CLI.hardware.update_firmware:cli'),
+    ('hardware:rescue', 'SoftLayer.CLI.hardware.power:rescue'),
 
     ('securitygroup', 'SoftLayer.CLI.securitygroup'),
     ('securitygroup:list', 'SoftLayer.CLI.securitygroup.list:cli'),
diff --git a/SoftLayer/transports.py b/SoftLayer/transports.py
index b3239fc1..32bda03e 100644
--- a/SoftLayer/transports.py
+++ b/SoftLayer/transports.py
@@ -99,12 +99,7 @@ def __init__(self, items, total_count):
 
 class XmlRpcTransport(object):
     """XML-RPC transport."""
-    def __init__(self,
-                 endpoint_url=None,
-                 timeout=None,
-                 proxy=None,
-                 user_agent=None,
-                 verify=True):
+    def __init__(self, endpoint_url=None, timeout=None, proxy=None, user_agent=None, verify=True):
 
         self.endpoint_url = (endpoint_url or
                              consts.API_PUBLIC_ENDPOINT).rstrip('/')
@@ -202,19 +197,13 @@ def __call__(self, request):
 class RestTransport(object):
     """REST transport.
 
-    Currently only supports GET requests (no POST, PUT, DELETE) and lacks
-    support for masks, filters, limits and offsets.
+    REST calls should mostly work, but is not fully tested.
+    XML-RPC should be used when in doubt
     """
 
-    def __init__(self,
-                 endpoint_url=None,
-                 timeout=None,
-                 proxy=None,
-                 user_agent=None,
-                 verify=True):
+    def __init__(self, endpoint_url=None, timeout=None, proxy=None, user_agent=None, verify=True):
 
-        self.endpoint_url = (endpoint_url or
-                             consts.API_PUBLIC_ENDPOINT_REST).rstrip('/')
+        self.endpoint_url = (endpoint_url or consts.API_PUBLIC_ENDPOINT_REST).rstrip('/')
         self.timeout = timeout or None
         self.proxy = proxy
         self.user_agent = user_agent or consts.USER_AGENT
@@ -223,12 +212,12 @@ def __init__(self,
     def __call__(self, request):
         """Makes a SoftLayer API call against the REST endpoint.
 
-        This currently only works with GET requests
+        REST calls should mostly work, but is not fully tested.
+        XML-RPC should be used when in doubt
 
         :param request request: Request object
         """
-        request.transport_headers.setdefault('Content-Type',
-                                             'application/json')
+        request.transport_headers.setdefault('Content-Type', 'application/json')
         request.transport_headers.setdefault('User-Agent', self.user_agent)
 
         params = request.headers.copy()
@@ -252,9 +241,8 @@ def __call__(self, request):
             )
 
         method = REST_SPECIAL_METHODS.get(request.method)
-        is_special_method = True
+
         if method is None:
-            is_special_method = False
             method = 'GET'
 
         body = {}
@@ -272,9 +260,7 @@ def __call__(self, request):
         if request.identifier is not None:
             url_parts.append(str(request.identifier))
 
-        # Special methods (createObject, editObject, etc) use the HTTP verb
-        # to determine the action on the resource
-        if request.method is not None and not is_special_method:
+        if request.method is not None:
             url_parts.append(request.method)
 
         url = '%s.%s' % ('/'.join(url_parts), 'json')


Test patch:
diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py
index aecda810..25de7151 100644
--- a/tests/CLI/modules/server_tests.py
+++ b/tests/CLI/modules/server_tests.py
@@ -445,3 +445,20 @@ def test_edit(self):
             args=(100,),
             identifier=100,
         )
+
+    @mock.patch('SoftLayer.CLI.formatting.confirm')
+    def test_rescue(self, confirm_mock):
+        confirm_mock.return_value = True
+        result = self.run_command(['server', 'rescue', '1000'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output, "")
+        self.assert_called_with('SoftLayer_Hardware_Server', 'bootToRescueLayer', identifier=1000)
+
+    @mock.patch('SoftLayer.CLI.formatting.confirm')
+    def test_server_rescue_negative(self, confirm_mock):
+        confirm_mock.return_value = False
+        result = self.run_command(['server', 'rescue', '1000'])
+
+        self.assertEqual(result.exit_code, 2)
+        self.assertIsInstance(result.exception, exceptions.CLIAbort)