Repository :
http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
---------------------------------------------------------------
commit 2ae0c00ab09d5ae31f19d65c2fc442ba4d1648e5
Author: Peter Hatina <phatina(a)redhat.com>
Date: Mon Sep 15 20:34:44 2014 +0200
fix missing indication reply
---------------------------------------------------------------
cli/lmi/shell/compat/CIMIndicationListener.py | 49 +++++++
cli/lmi/shell/compat/http.py | 189 +++++++++++++++++++++++++
2 files changed, 238 insertions(+), 0 deletions(-)
diff --git a/cli/lmi/shell/compat/CIMIndicationListener.py
b/cli/lmi/shell/compat/CIMIndicationListener.py
index d2d939e..9f717f8 100644
--- a/cli/lmi/shell/compat/CIMIndicationListener.py
+++ b/cli/lmi/shell/compat/CIMIndicationListener.py
@@ -22,12 +22,15 @@ import socket
import string
import threading
+from xml.sax.saxutils import escape
+
from SocketServer import BaseRequestHandler
from SocketServer import ThreadingMixIn
from BaseHTTPServer import HTTPServer
from BaseHTTPServer import BaseHTTPRequestHandler
from lmi.shell.LMIExceptions import ConnectionError
+from lmi.shell.compat import http
class CIMIndicationHandlerCallback(object):
@@ -55,6 +58,43 @@ class CIMIndicationHandler(ThreadingMixIn, BaseHTTPRequestHandler):
"""
CIMLISTENER_PREFIX = "/CIMListener"
+ @staticmethod
+ def make_indication_response(cim_version, dtd_version, message_id,
+ protocol_version):
+ """
+ Returns CIM-XML indication response with CIM, DTD, Protocol version
+ and Message ID filled.
+ """
+ return "<?xml version=\"1.0\" encoding=\"utf-8\"
?>\n" \
+ "<CIM CIMVERSION=\"%s\"
DTDVERSION=\"%s\">\n" \
+ "<MESSAGE ID=\"%s\"
PROTOCOLVERSION=\"%s\">\n" \
+ "<SIMPLEEXPRSP>\n" \
+ "<EXPMETHODRESPONSE NAME=\"ExportIndication\">\n"
\
+ "</EXPMETHODRESPONSE>\n" \
+ "</SIMPLEEXPRSP>\n" \
+ "</MESSAGE>\n" \
+ "</CIM>\n" % tuple(escape(arg) for arg in (
+ cim_version, dtd_version, message_id, protocol_version))
+
+ def send_indication_response(self, response):
+ """
+ Sends indication response.
+ """
+ http_version = self.request_version
+ http_response = http.HTTPResponse(200, http_version, wfile=self.wfile)
+ http_response[http.HEADER_NAME_CONTENT_TYPE] = http.HEADER_VALUE_CONTENT_TYPE
+ if http_version == http.HTTP_1_1:
+ http_response[http.HEADER_NAME_TRANSFER_ENCODING] =
http.HEADER_VALUE_CHUNKED
+ else:
+ http_response[http.HEADER_NAME_CONTENT_LENGTH] = len(response)
+ http_response[http.HEADER_NAME_CIM_EXPORT] =
http.HEADER_VALUE_CIM_EXPORT_METHOD_RESPONSE
+ http_response[http.HEADER_NAME_TRAILER] = ", ".join([
+ http.HEADER_VALUE_CIM_STATUS_CODE,
+ http.HEADER_VALUE_CIM_STATUS_CODE_DESCRIPTION,
+ http.HEADER_VALUE_CONTENT_LANGUAGE])
+ http_response.body = response
+ http_response.send()
+
def do_POST(self):
"""
Overridden method, which is called, when a indication is received. It
@@ -101,6 +141,15 @@ class CIMIndicationHandler(ThreadingMixIn, BaseHTTPRequestHandler):
if "NewIndication" in ind_dict:
ind = ind_dict["NewIndication"]
+ # Inform the CIMOM, that we got the indication.
+ cim_version = tt[1]["CIMVERSION"]
+ dtd_version = tt[1]["DTDVERSION"]
+ message_id = message[1]["ID"]
+ protocol_version = message[1]["PROTOCOLVERSION"]
+ self.send_indication_response(
+ self.make_indication_response(
+ cim_version, dtd_version, message_id, protocol_version))
+
path = self.path
cimlistener_ind = path.find(self.CIMLISTENER_PREFIX)
if cimlistener_ind != -1:
diff --git a/cli/lmi/shell/compat/http.py b/cli/lmi/shell/compat/http.py
new file mode 100644
index 0000000..1d86154
--- /dev/null
+++ b/cli/lmi/shell/compat/http.py
@@ -0,0 +1,189 @@
+# Copyright (C) 2012-2014 Peter Hatina <phatina(a)redhat.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <
http://www.gnu.org/licenses/>.
+
+from abc import ABCMeta
+from abc import abstractmethod
+from collections import OrderedDict
+from BaseHTTPServer import BaseHTTPRequestHandler
+
+# HTTP responses dict.
+responses = BaseHTTPRequestHandler.responses
+
+# HTTP string versions
+HTTP_0_9 = "HTTP/0.9"
+HTTP_1_0 = "HTTP/1.0"
+HTTP_1_1 = "HTTP/1.1"
+
+# HTTP headers with values.
+HEADER_NAME_CIM_EXPORT = "CIMExport"
+HEADER_NAME_CONTENT_LENGTH = "Content-Length"
+HEADER_NAME_CONTENT_TYPE = "Content-Type"
+HEADER_NAME_TRAILER = "Trailer"
+HEADER_NAME_TRANSFER_ENCODING = "transfer-encoding"
+HEADER_VALUE_CIM_EXPORT_METHOD_RESPONSE = "MethodResponse"
+HEADER_VALUE_CHUNKED = "chunked"
+HEADER_VALUE_CIM_STATUS_CODE = "CIMStatusCode"
+HEADER_VALUE_CIM_STATUS_CODE_DESCRIPTION = "CIMStatusCodeDescription"
+HEADER_VALUE_CONTENT_LANGUAGE = "Content-Language"
+HEADER_VALUE_CONTENT_TYPE = "application/xml; charset=utf-8"
+
+CRLF = "\r\n"
+
+
+class HTTPBase(object):
+ """
+ Base class for HTTP messages.
+ """
+ def __init__(self, wfile):
+ self.wfile = wfile
+
+
+class HTTPMessage(HTTPBase):
+ """
+ Class for sending a HTTP message.
+ """
+ __metaclass__ = ABCMeta
+
+ # RFC 2616 doesn't define chunk size limit. Let's pick one.
+ CHUNK_MAX = 4 * 1024
+
+ def __init__(self, http_version, headers=None, body=None, wfile=None):
+ super(HTTPMessage, self).__init__(wfile)
+ self.http_version = http_version
+ self.headers = OrderedDict(headers or {})
+ self.body = body or ""
+
+ def __getitem__(self, name):
+ """
+ Returns a HTTP header.
+ """
+ return self.headers[name]
+
+ def __setitem__(self, name, value):
+ """
+ Updates or adds a new HTTP header.
+ """
+ self.headers[name] = value
+
+ @staticmethod
+ def is_message_chunked(headers):
+ """
+ Returns True, if the headers contain "chunked" encoding entry.
+ """
+ return headers.get(
+ HEADER_NAME_TRANSFER_ENCODING) == \
+ HEADER_VALUE_CHUNKED
+
+ def send_crlf(self):
+ """
+ Sends CRLF symbols.
+ """
+ self.wfile.write(CRLF)
+
+ def send_chunk(self, chunk=""):
+ """
+ Sends a one chunk of the message.
+ """
+ self.wfile.write("%x" % len(chunk))
+ self.wfile.write(CRLF)
+ self.wfile.write(chunk)
+ self.wfile.write(CRLF)
+
+ def send_message_body(self, body):
+ """
+ Sends a response body.
+ """
+ if not self.is_message_chunked(self.headers) or \
+ self.http_version != HTTP_1_1:
+ self.wfile.write(body)
+ else:
+ pos = 0
+ msg_len = len(body)
+ while pos < msg_len:
+ if msg_len - pos >= self.CHUNK_MAX:
+ to_send = self.CHUNK_MAX
+ else:
+ to_send = msg_len - pos
+ # Send a chunk of data.
+ self.send_chunk(body[pos:pos+to_send])
+ pos += to_send
+
+ # Terminating chunk
+ self.send_chunk()
+
+ def send_header(self, name, value):
+ """
+ Sends a HTTP header.
+ """
+ if self.http_version == HTTP_0_9:
+ return
+ self.wfile.write("%s: %s" % (name, value))
+ self.send_crlf()
+
+ def send_end_headers(self):
+ """
+ Sends HTTP headers terminator.
+ """
+ if self.http_version == HTTP_0_9:
+ return
+ self.send_crlf()
+
+ def send_message_headers(self, headers):
+ """
+ Sends HTTP headers.
+ """
+ for name, value in headers.iteritems():
+ self.send_header(name, value)
+ self.send_end_headers()
+
+ @abstractmethod
+ def send(self, *args, **kwargs):
+ """
+ Sends either HTTP message.
+ """
+ pass
+
+
+class HTTPResponse(HTTPMessage):
+ """
+ HTTP Response class.
+ """
+ def __init__(self, code, http_version, headers=None, body=None, wfile=None):
+ super(HTTPResponse, self).__init__(http_version, headers, body, wfile)
+ self.code = code
+
+ def send_message_response_line(self, http_version, code):
+ """
+ Sends a HTTP response status line.
+ """
+ if http_version in (HTTP_1_0, HTTP_1_1):
+ self.wfile.write("%s %d" % (http_version, code))
+ if code in responses:
+ self.wfile.write(" %s" % responses[code][0])
+ self.send_crlf()
+ elif http_version == HTTP_0_9:
+ self.wfile.write("%s %d" % (http_version, code))
+ self.send_crlf()
+
+ def send(self, wfile=None):
+ """
+ Sends a response header and message.
+ """
+ if wfile is not None:
+ self.wfile = wfile
+ assert self.wfile is not None, "wfile can't be None"
+ self.send_message_response_line(self.http_version, self.code)
+ self.send_message_headers(self.headers)
+ self.send_message_body(self.body)