Add testrail_upload_suite tool

Change-Id: Id3055c83225d0dcd69015658f05bd9ce07e8d88d
Related-prod: PRODX-942
diff --git a/testrail_upload_suites/testrail.py b/testrail_upload_suites/testrail.py
new file mode 100644
index 0000000..a2c6523
--- /dev/null
+++ b/testrail_upload_suites/testrail.py
@@ -0,0 +1,104 @@
+#
+# TestRail API binding for Python 3.x (API v2, available since
+# TestRail 3.0)
+# Compatible with TestRail 3.0 and later.
+#
+# Learn more:
+#
+# http://docs.gurock.com/testrail-api2/start
+# http://docs.gurock.com/testrail-api2/accessing
+#
+# Copyright Gurock Software GmbH. See license.md for details.
+#
+
+import requests
+import json
+import base64
+
+
+class APIClient:
+    def __init__(self, base_url):
+        self.user = ''
+        self.password = ''
+        if not base_url.endswith('/'):
+            base_url += '/'
+        self.__url = base_url + 'index.php?/api/v2/'
+
+    #
+    # Send Get
+    #
+    # Issues a GET request (read) against the API and returns the result
+    # (as Python dict) or filepath if successful file download
+    #
+    # Arguments:
+    #
+    # uri                 The API method to call including parameters
+    #                     (e.g. get_case/1)
+    #
+    # filepath            The path and file name for attachment download
+    #                     Used only for 'get_attachment/:attachment_id'
+    #
+    def send_get(self, uri, filepath=None):
+        return self.__send_request('GET', uri, filepath)
+
+    #
+    # Send POST
+    #
+    # Issues a POST request (write) against the API and returns the result
+    # (as Python dict).
+    #
+    # Arguments:
+    #
+    # uri                 The API method to call including parameters
+    #                     (e.g. add_case/1)
+    # data                The data to submit as part of the request (as
+    #                     Python dict, strings must be UTF-8 encoded)
+    #                     If adding an attachment, must be the path
+    #                     to the file
+    #
+    def send_post(self, uri, data):
+        return self.__send_request('POST', uri, data)
+
+    def __send_request(self, method, uri, data):
+        url = self.__url + uri
+
+        auth = str(
+            base64.b64encode(
+                bytes('%s:%s' % (self.user, self.password), 'utf-8')
+            ),
+            'ascii'
+        ).strip()
+        headers = {'Authorization': 'Basic ' + auth}
+
+        if method == 'POST':
+            if uri[:14] == 'add_attachment':    # add_attachment API method
+                files = {'attachment': (open(data, 'rb'))}
+                response = requests.post(url, headers=headers, files=files)
+                files['attachment'].close()
+            else:
+                headers['Content-Type'] = 'application/json'
+                payload = bytes(json.dumps(data), 'utf-8')
+                response = requests.post(url, headers=headers, data=payload)
+        else:
+            headers['Content-Type'] = 'application/json'
+            response = requests.get(url, headers=headers)
+
+        if response.status_code > 201:
+            try:
+                error = response.json()
+            except:     # response.content not formatted as JSON
+                error = str(response.content)
+            raise APIError('TestRail API returned HTTP %s (%s)' % (response.status_code, error))
+        else:
+            if uri[:15] == 'get_attachment/':   # Expecting file, not JSON
+                try:
+                    open(data, 'wb').write(response.content)
+                    return (data)
+                except:
+                    return ("Error saving attachment.")
+            else:
+                return response.json()
+
+
+class APIError(Exception):
+    pass
\ No newline at end of file