You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2021/12/08 13:00:59 UTC

[libcloud] 03/06: Update AWS error response XML parsing code so we also handle situation where response doesn't have a namespace.

This is an automated email from the ASF dual-hosted git repository.

tomaz pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/libcloud.git

commit 7516dd4cbaa76c8ea93e63bc0c006a12b4fa5ff1
Author: Tomaz Muraus <to...@tomaz.me>
AuthorDate: Wed Dec 8 13:45:36 2021 +0100

    Update AWS error response XML parsing code so we also handle situation
    where response doesn't have a namespace.
    
    It looks like that in some cases AWS returns error response without a
    namespace which previous version of the code didn't handle correctly.
---
 libcloud/common/aws.py | 14 +++++++++++---
 libcloud/utils/xml.py  | 44 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/libcloud/common/aws.py b/libcloud/common/aws.py
index 8c415e9..09c0178 100644
--- a/libcloud/common/aws.py
+++ b/libcloud/common/aws.py
@@ -37,6 +37,8 @@ from libcloud.common.base import JsonResponse
 from libcloud.common.types import InvalidCredsError, MalformedResponseError
 from libcloud.utils.py3 import b, httplib, urlquote
 from libcloud.utils.xml import findtext, findall
+from libcloud.utils.xml import findall_ignore_namespace
+from libcloud.utils.xml import findtext_ignore_namespace
 
 __all__ = [
     "AWSBaseResponse",
@@ -79,8 +81,12 @@ class AWSBaseResponse(XmlResponse):
         :return: ``tuple`` with two elements: (code, message)
         :rtype: ``tuple``
         """
-        code = findtext(element=element, xpath="Code", namespace=self.namespace)
-        message = findtext(element=element, xpath="Message", namespace=self.namespace)
+        code = findtext_ignore_namespace(
+            element=element, xpath="Code", namespace=self.namespace
+        )
+        message = findtext_ignore_namespace(
+            element=element, xpath="Message", namespace=self.namespace
+        )
 
         return code, message
 
@@ -120,7 +126,9 @@ class AWSGenericResponse(AWSBaseResponse):
             )
 
         if self.xpath:
-            errs = findall(element=body, xpath=self.xpath, namespace=self.namespace)
+            errs = findall_ignore_namespace(
+                element=body, xpath=self.xpath, namespace=self.namespace
+            )
         else:
             errs = [body]
 
diff --git a/libcloud/utils/xml.py b/libcloud/utils/xml.py
index c819d7e..3337565 100644
--- a/libcloud/utils/xml.py
+++ b/libcloud/utils/xml.py
@@ -13,7 +13,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__all__ = ["fixxpath", "findtext", "findattr", "findall"]
+__all__ = [
+    "fixxpath",
+    "findtext",
+    "findattr",
+    "findall",
+    "findall_ignore_namespace",
+    "findtext_ignore_namespace",
+]
 
 
 def fixxpath(xpath, namespace=None):
@@ -36,9 +43,44 @@ def findtext(element, xpath, namespace=None, no_text_value=""):
     return value
 
 
+def findtext_ignore_namespace(element, xpath, namespace=None, no_text_value=""):
+    """
+    Special version of findtext() which first tries to find the provided value using the provided
+    namespace and in case no results are found we fallback to the xpath lookup without namespace.
+
+    This is needed because some providers return some responses with namespace and some without.
+    """
+
+    result = findtext(
+        element=element, xpath=xpath, namespace=namespace, no_text_value=no_text_value
+    )
+
+    if not result and namespace:
+        result = findtext(
+            element=element, xpath=xpath, namespace=None, no_text_value=no_text_value
+        )
+
+    return result
+
+
 def findattr(element, xpath, namespace=None):
     return element.findtext(fixxpath(xpath=xpath, namespace=namespace))
 
 
 def findall(element, xpath, namespace=None):
     return element.findall(fixxpath(xpath=xpath, namespace=namespace))
+
+
+def findall_ignore_namespace(element, xpath, namespace=None):
+    """
+    Special version of findall() which first tries to find the provided value using the provided
+    namespace and in case no results are found we fallback to the xpath lookup without namespace.
+
+    This is needed because some providers return some responses with namespace and some without.
+    """
+    result = findall(element=element, xpath=xpath, namespace=namespace)
+
+    if not result and namespace:
+        result = findall(element=element, xpath=xpath, namespace=None)
+
+    return result