From f8ecbaa71a9bf2fdf933c7977e09d812fbcfde8d Mon Sep 17 00:00:00 2001 From: Yue Zhang Date: Fri, 10 Oct 2014 18:43:26 +0800 Subject: [PATCH] Add http utils --- test/test_logger.py | 4 ++ test/test_rest_util.py | 48 ++++++++++++++++ walinuxagent/logger.py | 10 ++-- walinuxagent/utils/restutil.py | 100 +++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 test/test_rest_util.py create mode 100644 walinuxagent/utils/restutil.py diff --git a/test/test_logger.py b/test/test_logger.py index 00b537e..d3ec282 100644 --- a/test/test_logger.py +++ b/test/test_logger.py @@ -40,6 +40,10 @@ class TestLogger(unittest.TestCase): _logger.info("This is an exception {0}", Exception("Test")) _logger.info("This is an number {0}", 0) _logger.info("This is an boolean {0}", True) + _logger.verbose("{0} {1}", 0, 1) + _logger.info("{0} {1}", 0, 1) + _logger.warn("{0} {1}", 0, 1) + _logger.error("{0} {1}", 0, 1) def test_file_appender(self): appender_config = logger.AppenderConfig({'type':'FILE', 'level':'INFO', 'file_path':'/tmp/log'}) diff --git a/test/test_rest_util.py b/test/test_rest_util.py new file mode 100644 index 0000000..0bba673 --- /dev/null +++ b/test/test_rest_util.py @@ -0,0 +1,48 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import env +import test.tools as tools +import uuid +import unittest +import os +import walinuxagent.utils.restutil as restutil +import test + +class TestHttpOperations(unittest.TestCase): + def test_parse_url(self): + host, action = restutil._ParseUrl("http://abc.def/ghi?jkl=mn") + self.assertEquals("abc.def", host) + self.assertEquals("/ghi?jkl=mn", action) + host, action = restutil._ParseUrl("http://abc.def/") + self.assertEquals("abc.def", host) + self.assertEquals("/", action) + + def test_http_get(self): + resp = restutil.HttpGet("http://httpbin.org/get") + self.assertNotEquals(None, resp) + + msg = str(uuid.uuid4()) + resp = restutil.HttpGet("http://httpbin.org/get", {"x-abc":msg}) + self.assertNotEquals(None, resp) + self.assertTrue(msg in resp) + +if __name__ == '__main__': + unittest.main() diff --git a/walinuxagent/logger.py b/walinuxagent/logger.py index 67bbd63..0a009b7 100644 --- a/walinuxagent/logger.py +++ b/walinuxagent/logger.py @@ -28,19 +28,19 @@ class Logger(object): self.appenders = [] def verbose(self, msg_format, *args): - self.log("VERBOSE", msg_format, args) + self.log("VERBOSE", msg_format, *args) def info(self, msg_format, *args): - self.log("INFO", msg_format, args) + self.log("INFO", msg_format, *args) def warn(self, msg_format, *args): - self.log("WARNING", msg_format, args) + self.log("WARNING", msg_format, *args) def error(self, msg_format, *args): - self.log("ERROR", msg_format, args) + self.log("ERROR", msg_format, *args) def log(self, level, msg_format, *args): - msg = msg_format.format(args) + msg = msg_format.format(*args) time = datetime.now().strftime('%Y/%m/%d %H:%M:%S.%f') log_item = "{0} {1} {2}".format(time, level, msg) for appender in self.appenders: diff --git a/walinuxagent/utils/restutil.py b/walinuxagent/utils/restutil.py new file mode 100644 index 0000000..8b36897 --- /dev/null +++ b/walinuxagent/utils/restutil.py @@ -0,0 +1,100 @@ +# Windows Azure Linux Agent +# +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +import platform +import os +import subprocess +import walinuxagent.logger as logger +import httplib +from urlparse import urlparse + +""" +REST api util functions +""" +__RetryWaitingInterval=10 + +def _ParseUrl(url): + o = urlparse(url) + action = o.path + if o.query: + action = "{0}?{1}".format(action, o.query) + if o.fragment: + action = "{0}#{1}".format(action, o.fragment) + + return o.netloc, action + +def _HttpRequest(method, host, action, data=None, headers=None): + resp = None; + try: + httpConnection = httplib.HTTPConnection(host) + if headers == None: + httpConnection.request(method, action, data) + else: + httpConnection.request(method, action, data, headers) + resp = httpConnection.getresponse() + except httplib.HTTPException, e: + logger.Error('HTTPException {0}, args:{1}', e, repr(e.args)) + except IOError, e: + logger.Error('Socket IOError {0}, args:{1}', e, repr(e.args)) + return resp + +def HttpRequest(method, url, data, headers=None, maxRetry=0): + """ + Sending http request to server + On error, sleep 10 and maxRetry times. + Return the output buffer or None. + """ + logger.Verbose("{0} {1}", method, url) + host, action = _ParseUrl(url) + resp = _HttpRequest(method, host, action, data, headers) + for retry in range(0, maxRetry): + if resp and resp.status == httplib.OK: + break; + else: + logger.Error("Retry={0}, Status={1}, {2} {3}{4}", retry, + resp.status, method, host, action) + time.sleep(__RetryWaitingInterval) + resp = _HttpRequest(method, host, action, data, headers) + + if resp and (resp.status == httplib.OK or resp.status == httplib.ACCEPTED): + return resp.read() + else: + return None + +def HttpGet(url, headers=None, maxRetry=0): + return HttpRequest("GET", url, None, headers, maxRetry) + +def HttpPost(url, data, headers=None, maxRetry=0): + return HttpRequest("POST", url, data, headers, maxRetry) + +def HttpPut(url, data, headers=None, maxRetry=0): + return HttpRequest("PUT", url, data, headers, maxRetry) + +def HttpDelete(url, data, headers=None, maxRetry=0): + return HttpRequest("DELETE", url, data, headers, maxRetry) + +def HttpPutBlockBlob(url, data, maxRetry): + headers = { + "x-ms-blob-type" : "BlockBlob", + "x-ms-date" : time.strftime("%Y-%M-%dT%H:%M:%SZ", time.gmtime()), + "Content-Length": str(len(data)) + } + return HttpPut(url, data, headers, maxRetry) + +#End REST api util functions