From: rmccabe@sourceware.org <rmccabe@sourceware.org>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] conga/luci/site/luci/Extensions ricci_communic ...
Date: 3 Jul 2007 16:56:40 -0000 [thread overview]
Message-ID: <20070703165640.31192.qmail@sourceware.org> (raw)
CVSROOT: /cvs/cluster
Module name: conga
Changes by: rmccabe at sourceware.org 2007-07-03 16:56:39
Modified files:
luci/site/luci/Extensions: ricci_communicator.py
Log message:
Cleanups and better debug messages
Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_communicator.py.diff?cvsroot=cluster&r1=1.27&r2=1.28
--- conga/luci/site/luci/Extensions/ricci_communicator.py 2007/06/27 08:14:13 1.27
+++ conga/luci/site/luci/Extensions/ricci_communicator.py 2007/07/03 16:56:39 1.28
@@ -5,11 +5,10 @@
# GNU General Public License as published by the
# Free Software Foundation.
-import xml
-import xml.dom
from xml.dom import minidom
-from LuciSyslog import get_logger
+from xml.dom.Node import ELEMENT_NODE
from conga_ssl import SSLSocket
+from LuciSyslog import get_logger
from conga_constants import LUCI_DEBUG_MODE, LUCI_DEBUG_NET
CERTS_DIR_PATH = '/var/lib/luci/var/certs/'
@@ -37,8 +36,8 @@
self.__timeout_init)
if enforce_trust:
if not self.ss.trusted():
- luci_log.info('The SSL certificate for host "%s" is not trusted. Aborting connection attempt.' % self.__hostname)
- raise RicciError, 'ricci\'s certificate is not trusted'
+ luci_log.info('The SSL certificate for "%s:%d" is not trusted. Aborting connection attempt.' % (self.__hostname, self.__port))
+ raise RicciError, 'The SSL certificate for %s:%d is not trusted' % (self.__hostname, self.__port)
except Exception, e:
errmsg = 'Unable to establish an SSL connection to %s:%d: %s' \
% ((self.__hostname, self.__port, str(e)))
@@ -49,8 +48,8 @@
hello = self.__receive(self.__timeout_init)
try:
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:init0: recv header from %s: \"%s\"' \
- % (self.__hostname, hello.toxml()))
+ luci_log.debug_net('RC:init0: recv header from %s:%d "%s"' \
+ % (self.__hostname, self.__port, hello.toxml()))
except:
if LUCI_DEBUG_MODE is True:
luci_log.debug_verbose('RC:init0: error receiving header from %s:%d: %r %s' % (self.__hostname, self.__port, e, str(e)))
@@ -67,47 +66,47 @@
def hostname(self):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:hostname: [auth %d] hostname = %s' \
- % (self.__authed, self.__hostname))
+ luci_log.debug_net('RC:hostname: [auth=%d] hostname = %s [%d]' \
+ % (self.__authed, self.__hostname, self.__port))
return self.__hostname
def authed(self):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:authed: reported authed = %d for %s' \
- % (self.__authed, self.__hostname))
+ luci_log.debug_net('RC:authed: reported authed = %d for %s:%d' \
+ % (self.__authed, self.__hostname, self.__port))
return self.__authed
def system_name(self):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:SN: [auth %d] system_name = %s for %s' \
- % (self.__authed, self.__reported_hostname, self.__hostname))
+ luci_log.debug_net('RC:SN: [auth=%d] system_name = %s for %s:%d' \
+ % (self.__authed, self.__reported_hostname, self.__hostname, self.__port))
return self.__reported_hostname
def cluster_info(self):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:CI: [auth %d] (%s,%s) for %s' \
+ luci_log.debug_net('RC:CI: [auth=%d] (%s,%s) for %s:%d' \
% ( self.__authed, self.__cluname,
- self.__clualias, self.__hostname))
+ self.__clualias, self.__hostname, self.__port))
return (self.__cluname, self.__clualias)
def os(self):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:OS: [auth %d] reported os = %s for %s' \
- % (self.__authed, self.__os, self.__hostname))
+ luci_log.debug_net('RC:OS: [auth=%d] reported os = %s for %s:%d' \
+ % (self.__authed, self.__os, self.__hostname, self.__port))
return self.__os
def dom0(self):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:DOM0: [auth %d] dom0 = %s for %s' \
- % (self.__authed, self.__dom0, self.__hostname))
+ luci_log.debug_net('RC:DOM0: [auth=%d] dom0 = %s for %s:%d' \
+ % (self.__authed, self.__dom0, self.__hostname, self.__port))
return self.__dom0
def fingerprint(self):
fp = self.ss.peer_fingerprint()
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:fp: [auth %d] fp for %s = %s' \
- % (self.__authed, self.__hostname, fp))
+ luci_log.debug_net('RC:fp: [auth=%d] fp for %s:%d = %s' \
+ % (self.__authed, self.__hostname, self.__port, fp))
return fp
def trust(self):
@@ -122,16 +121,16 @@
def auth(self, password):
if self.authed():
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RC:AUTH0: already authenticated to %s' \
- % self.__hostname)
+ luci_log.debug_verbose('RC:AUTH0: already authed to %s:%d' \
+ % (self.__hostname, self.__port))
return True
# send request
doc = minidom.Document()
- ricci = doc.createElement("ricci")
- ricci.setAttribute("version", "1.0")
- ricci.setAttribute("function", "authenticate")
- ricci.setAttribute("password", password)
+ ricci = doc.createElement('ricci')
+ ricci.setAttribute('version', '1.0')
+ ricci.setAttribute('function', 'authenticate')
+ ricci.setAttribute('password', password)
doc.appendChild(ricci)
self.__send(doc, self.__timeout_auth)
@@ -148,12 +147,12 @@
self.__dom0 = resp.firstChild.getAttribute('xen_host') == 'true'
except Exception, e:
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RC:AUTH1: %r %s for %s' \
- % (e, str(e), self.__hostname))
+ luci_log.debug_verbose('RC:AUTH1: %r %s for %s:%d' \
+ % (e, str(e), self.__hostname, self.__port))
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:auth1: auth call returning %d' \
- % self.__authed)
+ luci_log.debug_net('RC:auth1: auth call to %s:%d returning %d' \
+ % (self.__hostname, self.__port, self.__authed))
return self.__authed
def unauth(self):
@@ -168,8 +167,8 @@
try:
ret = resp.firstChild.getAttribute('success')
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:UNAUTH0: unauthenticate %s for %s' \
- % (ret, self.__hostname))
+ luci_log.debug_net('RC:UNAUTH0: unauthenticate %s for %s:%d' \
+ % (ret, self.__hostname, self.__port))
if ret != '0':
raise Exception, 'Invalid response'
@@ -178,8 +177,8 @@
except:
pass
except:
- errstr = 'Error authenticating host %s: %s' \
- % (self.__hostname, str(ret))
+ errstr = 'Error authenticating to the ricci agent at %s:%d: %s' \
+ % (self.__hostname, self.__port, str(ret))
luci_log.info(errstr)
raise RicciError, errstr
return True
@@ -187,15 +186,16 @@
def process_batch(self, batch_xml, async=False):
try:
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:PB0: [auth=%d] to %s for batch %s [async=%d]' % (self.__authed, self.__hostname, batch_xml.toxml(), async))
+ luci_log.debug_net_priv('RC:PB0: [auth=%d async=%d] to %s:%d for batch "%s"' % (self.__authed, async, self.__hostname, self.__port, batch_xml.toxml()))
except:
pass
if not self.authed():
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RC:PB1: not authenticated to %s' \
- % self.__hostname)
- raise RicciError, 'not authenticated to host %s' % self.__hostname
+ luci_log.debug_verbose('RC:PB1: not authenticated to %s:%d' \
+ % (self.__hostname, self.__port))
+ raise RicciError, 'not authenticated to the ricci agent at %s:%d' \
+ % (self.__hostname, self.__port)
# construct request
doc = minidom.Document()
@@ -218,34 +218,37 @@
errstr = 'Error sending XML batch command to %s:%d: %s' \
% (self.__hostname, self.__port, str(e))
luci_log.info(errstr)
+
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:PB2: Error sending XML \"%s\" to host %s' % (doc.toxml(), self.__hostname))
+ luci_log.debug_net_priv('RC:PB2: sending "%s" to %s:%d: %r %s' \
+ % (doc.toxml(), self.__hostname, self.__port, e, str(e)))
raise RicciError, errstr
# receive response
doc = self.__receive(self.__timeout_long)
if LUCI_DEBUG_NET is True:
try:
- luci_log.debug_net_priv('RC:PB3: received from %s XML \"%s\"' \
- % (self.__hostname, doc.toxml()))
+ luci_log.debug_net_priv('RC:PB3: XML from %s:%d: "%s"' \
+ % (self.__hostname, self.__port, doc.toxml()))
except:
pass
if doc.firstChild.getAttribute('success') != '0':
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RC:PB4: batch command failed')
- raise RicciError, 'The last ricci command to host %s:%d failed' \
+ luci_log.debug_verbose('RC:PB4: batch command to %s:%d failed' \
+ % (self.__hostname, self.__port))
+ raise RicciError, 'The last ricci command sent to %s:%d failed' \
% (self.__hostname, self.__port)
batch_node = None
for node in doc.firstChild.childNodes:
- if node.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node.nodeType == ELEMENT_NODE:
if node.nodeName == 'batch':
batch_node = node#.cloneNode(True)
if batch_node is None:
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RC:PB5: no <batch/> from %s' \
- % self.__hostname)
+ luci_log.debug_verbose('RC:PB5: no <batch/> from %s:%d' \
+ % (self.__hostname, self.__port))
raise RicciError, 'missing <batch/> in ricci response from %s:%d' \
% (self.__hostname, self.__port)
@@ -256,23 +259,28 @@
batch_xml_str = '<?xml version="1.0" ?><batch>%s</batch>' \
% batch_str
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:BRun0: attempting batch "%s" for host "%s"' % (batch_xml_str, self.__hostname))
+ luci_log.debug_net_priv('RC:BRun0: run batch "%s" at %s:%d' \
+ % (batch_xml_str, self.__hostname, self.__port))
batch_xml = minidom.parseString(batch_xml_str).firstChild
except Exception, e:
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:BRun1: received invalid batch XML for %s: "%s": "%s"' % (self.__hostname, batch_xml_str, str(e)))
- raise RicciError, 'batch XML is malformed'
+ luci_log.debug_net_priv('RC:BRun1: bad XML %s:%d: %r %s: "%s"' \
+ % (self.__hostname, self.__port, e, str(e), batch_xml_str))
+ raise RicciError, 'batch XML from %s:%d is malformed' \
+ % (self.__hostname, self.__port)
try:
ricci_xml = self.process_batch(batch_xml, async)
if LUCI_DEBUG_NET is True:
try:
- luci_log.debug_net_priv('RC:BRun2: received XML "%s" from host %s in response to batch command' % (ricci_xml.toxml(), self.__hostname))
+ luci_log.debug_net_priv('RC:BRun2: recv "%s" from %s:%d' \
+ % (ricci_xml.toxml(), self.__hostname, self.__port))
except:
pass
except:
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:BRun3: An error occurred while trying to process the batch job: "%s"' % batch_xml_str)
+ luci_log.debug_net_priv('RC:BRun3: error for "%s" at %s:%d' \
+ % (batch_xml_str, self.__hostname, self.__port))
return None
doc = minidom.Document()
@@ -287,24 +295,26 @@
batch = self.batch_report(batch_id)
if batch is None:
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RCCB0: batch id %s not found' \
- % batch_id)
- return (True, 'batch id %s was not found' % batch_id)
+ luci_log.debug_verbose('RCCB0: no id %s at %s:%d' \
+ % (batch_id, self.__hostname, self.__port))
+ return (True, 'batch id %s was not found at %s:%d' \
+ % (batch_id, self.__hostname, self.__port))
except Exception, e:
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RCCB1: %s:%d: %r %s' \
+ luci_log.debug_verbose('RCCB1: status: %s:%d: %r %s' \
% (self.__hostname, self.__port, e, str(e)))
return (-1, err_msg)
try:
code, new_err_msg = extract_module_status(batch, 1)
if new_err_msg:
- err_msg = 'A ricci error occurred on %s:%d: %s' \
- % (self.__hostname, self.__port, str(new_err_msg))
+ err_msg = 'Unable to retrieve batch %s status from %s:%d: %s' \
+ % (batch_id, self.__hostname, self.__port, str(new_err_msg))
+ luci_log.info(err_msg)
except Exception, e:
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RCCB2: %s:%d %r %s: %s' \
- % (self.__hostname, self.__port, e, str(e), err_msg))
+ luci_log.debug_verbose('RCCB2: batch %s stat on %s:%d: %r %s' \
+ % (batch_id, self.__hostname, self.__port, e, str(e)))
return (-1, err_msg)
# In progress.
@@ -317,23 +327,25 @@
# Error
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RCCB3: %s:%d code %d: %s' \
- % (self.__hostname, self.__port, code, err_msg))
+ luci_log.debug_verbose('RCCB3: batch %s on %s:%d code %d: %s' \
+ % (batch_id, self.__hostname, self.__port, code, err_msg))
return (-1, err_msg)
def batch_report(self, batch_id):
if LUCI_DEBUG_NET is True:
- luci_log.debug_net('RC:BRep0: [auth=%d] asking for batchid# %s for host %s' % (self.__authed, batch_id, self.__hostname))
+ luci_log.debug_net('RC:BRep0: [auth=%d] report for %s on %s:%d' \
+ % (self.__authed, batch_id, self.__hostname, self.__port))
if not self.authed():
- raise RicciError, 'Not authenticated to host %s' % self.__hostname
+ raise RicciError, 'Not authenticated to the ricci agent at %s:%d' \
+ % (self.__hostname, self.__port)
# construct request
doc = minidom.Document()
- ricci = doc.createElement("ricci")
- ricci.setAttribute("version", "1.0")
- ricci.setAttribute("function", "batch_report")
- ricci.setAttribute("batch_id", str(batch_id))
+ ricci = doc.createElement('ricci')
+ ricci.setAttribute('version', '1.0')
+ ricci.setAttribute('function', 'batch_report')
+ ricci.setAttribute('batch_id', str(batch_id))
doc.appendChild(ricci)
# send request
@@ -344,14 +356,17 @@
if doc.firstChild.getAttribute('success') == '12':
return None
if doc.firstChild.getAttribute('success') != '0':
- raise RicciError, 'Error while retrieving batch report for batch #%d from host %s' % (batch_id, self.__hostname)
+ raise RicciError, 'Error retrieving batch %s report from %s:%d' \
+ % (batch_id, self.__hostname, self.__port)
+
batch_node = None
for node in doc.firstChild.childNodes:
- if node.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node.nodeType == ELEMENT_NODE:
if node.nodeName == 'batch':
batch_node = node#.cloneNode(True)
if batch_node is None:
- raise RicciError, 'Missing <batch/> in ricci\'s response from host %s' % self.__hostname
+ raise RicciError, 'Missing <batch/> in ricci response from %s:%d' \
+ % (self.__hostname, self.__port)
return batch_node
@@ -360,18 +375,19 @@
self.ss.send(xml_doc.toxml(), timeout)
self.ss.send('\n', timeout)
except Exception, e:
+ errstr = 'Error sending batch command to %s:%d: %s' \
+ % (self.__hostname, self.__port, str(e))
+ luci_log.info(errstr)
+
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:send0: sending XML "%s" to %s: %s' \
- % (xml_doc.toxml(), self.__hostname, str(e)))
- luci_log.info('Error sending batch command to %s:%d: %s' \
- % (self.__hostname, self.__port, str(e)))
- raise RicciError, 'Write error while sending XML to host %s' \
- % self.__hostname
+ luci_log.debug_net_priv('RC:send0: send XML "%s" to %s:%d: %s' \
+ % (xml_doc.toxml(), self.__hostname, self.__port, str(e)))
+ raise RicciError, errstr
if LUCI_DEBUG_NET is True:
try:
- luci_log.debug_net_priv('RC:send1: Sent XML "%s" to host %s' \
- % (xml_doc.toxml(), self.__hostname))
+ luci_log.debug_net_priv('RC:send1: Sent XML "%s" to %s:%d' \
+ % (xml_doc.toxml(), self.__hostname, self.__port))
except:
pass
return
@@ -387,35 +403,37 @@
raise RicciError, errstr
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:recv1: Received XML "%s" from host %s' \
- % (xml_in, self.__hostname))
+ luci_log.debug_net_priv('RC:recv1: recv XML "%s" from %s:%d' \
+ % (xml_in, self.__hostname, self.__port))
doc = None
try:
doc = minidom.parseString(xml_in)
except Exception, e:
- errstr = 'Error parsing XML from host %s:%d: %s' \
+ errstr = 'Error parsing XML from %s:%d: %s' \
% (self.__hostname, self.__port, str(e))
luci_log.info(errstr)
+
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:recv2: parsing XML "%s" from %s' \
- % (xml_in, str(e)))
+ luci_log.debug_net_priv('RC:recv2: "%s" from %s:%d: %r %s' \
+ % (xml_in, self.__hostname, self.__port, e, str(e)))
raise RicciError, errstr
if not doc or not doc.firstChild:
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('RC:recv3: empty XML response from %s' \
- % self.__hostname)
- raise RicciError, 'Error an empty response was received from host %s' % self.__hostname
+ luci_log.debug_verbose('RC:recv3: empty XML from %s:%d' \
+ % (self.__hostname, self.__port))
+ raise RicciError, 'An empty XML response was received from %s:%d' \
+ % (self.__hostname, self.__port)
try:
if doc.firstChild.nodeName != 'ricci':
if LUCI_DEBUG_NET is True:
- luci_log.debug_net_priv('RC:recv4: Expecting "<ricci>" tag, got XML "%s" from %s' % (xml_in, self.__hostname))
- raise Exception, 'Expected first XML child node to be "<ricci>"'
+ luci_log.debug_net_priv('RC:recv4: Expecting "<ricci>" tag, got XML "%s" from %s:%d' % (xml_in, self.__hostname, self.__port))
+ raise Exception, 'expected first XML child node to be "<ricci>"'
except Exception, e:
- raise RicciError, 'Invalid XML ricci response from host %s' \
- % self.__hostname
+ raise RicciError, 'Invalid ricci XML response from %s:%d' \
+ % (self.__hostname, self.__port)
return doc
def get_ricci_communicator(self, hostname, allowed_systems):
@@ -424,7 +442,8 @@
if not self.access_to_host_allowed(hostname, allowed_systems):
if LUCI_DEBUG_MODE is True:
- luci_log.debug_verbose('GRC0: access to host %s is not allowed' % hostname)
+ luci_log.debug_verbose('GRC0: access to host %s is not allowed' \
+ % hostname)
return None
try:
@@ -433,9 +452,9 @@
raise Exception, 'initialization failed'
return rc
except Exception, e:
- luci_log.info('Error creating a ricci connection to %s: %r %s' \
- % (hostname, e, str(e)))
- return None
+ luci_log.info('Error creating a ricci connection to %s: %s' \
+ % (hostname, str(e)))
+ return None
def ricci_get_called_hostname(self, ricci):
return ricci.hostname()
@@ -472,7 +491,7 @@
if batch_xml.nodeName != 'batch':
if LUCI_DEBUG_NET is True:
try:
- luci_log.debug_net_priv('RC:BS0: batch node. Got \"%s\"' \
+ luci_log.debug_net_priv('RC:BS0: batch node. Got "%s"' \
% batch_xml.toxml())
except:
pass
@@ -481,7 +500,7 @@
total = 0
last = 0
for node in batch_xml.childNodes:
- if node.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node.nodeType == ELEMENT_NODE:
if node.nodeName == 'module':
total = total + 1
status = node.getAttribute('status')
@@ -530,7 +549,7 @@
c = 0
for node in batch_xml.childNodes:
- if node.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node.nodeType == ELEMENT_NODE:
if node.nodeName == 'module':
module_xml = node
c = c + 1
@@ -539,17 +558,17 @@
if status == '0' or status == '4':
# module executed, dig deeper into request
for node_i in module_xml.childNodes:
- if node_i.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node_i.nodeType == ELEMENT_NODE:
if node_i.nodeName == 'API_error':
return -2, 'API error'
elif node_i.nodeName == 'response':
for node_j in node_i.childNodes:
- if node_j.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if node_j.nodeType == ELEMENT_NODE:
if node_j.nodeName == 'function_response':
code = -11111111
msg = 'BUG'
for var in node_j.childNodes:
- if var.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if var.nodeType == ELEMENT_NODE:
if var.nodeName == 'var':
if var.getAttribute('name') == 'success' and var.getAttribute('value') == 'true':
return 0, ''
@@ -568,4 +587,5 @@
elif status == '5':
return -103, 'module removed from schedule'
- raise RicciError, 'no %dth module in the batch, or malformed response' % module_num
+ raise RicciError, 'no module %d in the batch, or malformed response' \
+ % module_num
next reply other threads:[~2007-07-03 16:56 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-07-03 16:56 rmccabe [this message]
-- strict thread matches above, loose matches on Subject: below --
2007-07-26 4:23 [Cluster-devel] conga/luci/site/luci/Extensions ricci_communic rmccabe
2006-10-20 18:37 rmccabe
2006-10-10 21:07 kupcevic
2006-09-26 21:04 kupcevic
2006-06-21 23:07 rmccabe
2006-06-19 17:10 rmccabe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070703165640.31192.qmail@sourceware.org \
--to=rmccabe@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.