From: rmccabe@sourceware.org <rmccabe@sourceware.org>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] conga/luci cluster/form-chooser cluster/form-m ...
Date: 23 Feb 2007 22:07:47 -0000 [thread overview]
Message-ID: <20070223220747.24209.qmail@sourceware.org> (raw)
CVSROOT: /cvs/cluster
Module name: conga
Changes by: rmccabe at sourceware.org 2007-02-23 22:07:45
Modified files:
luci/cluster : form-chooser form-macros
luci/homebase : luci_homebase.css
luci/site/luci/Extensions: cluster_adapters.py
conga_constants.py ricci_bridge.py
Added files:
luci/cluster : validate_sys_svc.js
luci/plone-custom: conga_ajax.js svc_reload.png
svc_reload_active.png svc_reload_disabled.png
svc_start.png svc_start_active.png
svc_start_disabled.png svc_stop.png
svc_stop_active.png svc_stop_disabled.png
luci/site/luci/Extensions: system_adapters.py
Log message:
- System service management interface (chkconfig / service start/stop/restart)
Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_sys_svc.js.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/form-chooser.diff?cvsroot=cluster&r1=1.16&r2=1.17
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/form-macros.diff?cvsroot=cluster&r1=1.192&r2=1.193
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/luci_homebase.css.diff?cvsroot=cluster&r1=1.39&r2=1.40
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/conga_ajax.js.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_reload.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_reload_active.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_reload_disabled.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_start.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_start_active.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_start_disabled.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_stop.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_stop_active.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/svc_stop_disabled.png.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/system_adapters.py.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/cluster_adapters.py.diff?cvsroot=cluster&r1=1.243&r2=1.244
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/conga_constants.py.diff?cvsroot=cluster&r1=1.36&r2=1.37
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_bridge.py.diff?cvsroot=cluster&r1=1.58&r2=1.59
/cvs/cluster/conga/luci/cluster/validate_sys_svc.js,v --> standard output
revision 1.1
--- conga/luci/cluster/validate_sys_svc.js
+++ - 2007-02-23 22:07:45.984856000 +0000
@@ -0,0 +1,22 @@
+function svc_callback() {
+ if (xmlHttp_object.readyState == 4) {
+ if (xmlHttp_object.status == 200) {
+ var response = xmlHttp_object.responseXML;
+ if (response) {
+ req_status = response.getAttribute('success');
+ req_op = response.getAttribute('operation');
+ req_svc = response.getAttribute('service');
+ req_msg = response.getAttribute('message');
+ //alert(req_msg + ' / ' + req_status + ' / ' + req_op + ' / ' + req_svc);
+ } else {
+ alert(xmlHttp_object.responseText);
+ }
+ } else {
+ //alert('Error retrieving data from server: ' + xmlHttp_object.status);
+ }
+ }
+}
+
+function update_sys_svc(url) {
+ initiate_async_get(url, svc_callback);
+}
--- conga/luci/cluster/form-chooser 2007/02/01 20:27:33 1.16
+++ conga/luci/cluster/form-chooser 2007/02/23 22:07:45 1.17
@@ -149,11 +149,15 @@
<span tal:omit-tag="" tal:condition="python: ptype == '55'">
<div metal:use-macro="here/form-macros/macros/fencedevprocess-form"/>
</span>
-
<span tal:omit-tag="" tal:condition="python: ptype == '80'">
<div metal:use-macro="here/form-macros/macros/conf_editor-form"/>
</span>
-
+ <span tal:omit-tag="" tal:condition="python: ptype == '90'">
+ <div metal:use-macro="here/form-macros/macros/system-svc-form"/>
+ </span>
+ <span tal:omit-tag="" tal:condition="python: ptype == '91'">
+ <div metal:use-macro="here/form-macros/macros/system-svc-update-form"/>
+ </span>
</span>
</metal:choose-form>
</body>
--- conga/luci/cluster/form-macros 2007/02/20 23:06:59 1.192
+++ conga/luci/cluster/form-macros 2007/02/23 22:07:45 1.193
@@ -5022,10 +5022,110 @@
</div>
</div>
+
<div metal:define-macro="fencedevprocess-form">
<h2>Fence Device Process Form</h2>
</div>
+<div metal:define-macro="system-svc-form">
+ <script type="text/javascript"
+ src="conga_ajax.js">
+ </script>
+ <script type="text/javascript"
+ src="/luci/cluster/validate_sys_svc.js">
+ </script>
+ <h2>Configure System Services</h2>
+
+ <form name="svcform" method="post" action="">
+
+ <input type="hidden" name="pagetype"
+ tal:attributes="value request/pagetype | nothing" />
+
+ <input type="hidden" name="systemname"
+ tal:attributes="value request/systemname | nothing" />
+
+ <table class="systemsTable"
+ tal:define="svcinfo python:'systemname' in request and here.get_sys_svc_list(request, request['systemname']) or []">
+ <tr class="systemsTable">
+ <th class="systemsTable">Name</th>
+ <th class="systemsTable">State</th>
+ <th class="systemsTable">Enabled at boot</th>
+ <th></th>
+ </tr>
+
+ <tr tal:repeat="s svcinfo">
+ <td class="systemsTable">
+ <span tal:content="s/name |string:[unknown]"
+ tal:attributes="title s/desc | nothing" />
+ </td>
+
+ <td class="systemsTable">
+ <span
+ tal:content="s/state |string:[unknown]"
+ tal:attributes="id python: '__STATUS__:' + s['name']" />
+ </td>
+
+ <td class="systemsTable">
+ <input type="checkbox"
+ tal:attributes="
+ name s/name;
+ id s/name;
+ checked python: s['enabled'] and 'checked' or ''"
+ />
+ </td>
+
+ <td class="systemsTable"
+ tal:define="
+ restart_disabled python:not s['running'] and 'disabled' or '';
+ start_disabled python:s['running'] and 'disabled' or '';
+ stop_disabled python:not s['running'] and 'disabled' or ''">
+
+ <input type="button"
+ title="start this service"
+ class="svc_control svc_control_start"
+ onfocus="if (this.blur) this.blur()"
+ tal:attributes="
+ onclick python: 'update_sys_svc(\'' + s['starturl'] + '\')';
+ id python: '__START__' + s['name'];
+ name python: '__START__' + s['name'];
+ disabled start_disabled" />
+
+ <input type="button"
+ title="restart this service"
+ class="svc_control svc_control_restart"
+ onfocus="if (this.blur) this.blur()"
+ tal:attributes="
+ onclick python: 'update_sys_svc(\'' + s['restarturl'] + '\')';
+ id python: '__RESTART__' + s['name'];
+ name python: '__RESTART__' + s['name'];
+ disabled restart_disabled" />
+
+ <input type="button"
+ title="stop this service"
+ class="svc_control svc_control_stop"
+ onfocus="if (this.blur) this.blur()"
+ tal:attributes="
+ onclick python: 'update_sys_svc(\'' + s['stopurl'] + '\')';
+ id python: '__STOP__' + s['name'];
+ name python: '__STOP__' + s['name'];
+ disabled stop_disabled" />
+ </td>
+ </tr>
+ <tr class="systemsTable">
+ <td>
+ <div class="hbSubmit">
+ <input type="submit" value="Update" />
+ </div>
+ </td>
+ </tr>
+ </table>
+ </form>
+</div>
+
+<div metal:define-macro="system-svc-update-form">
+ <tal:block tal:define="ret python: here.validate_manage_svc(request)" />
+</div>
+
<div metal:define-macro="conf_editor-form">
<h2>Edit cluster.conf</h2>
<form method="post"
--- conga/luci/homebase/luci_homebase.css 2007/02/08 05:05:22 1.39
+++ conga/luci/homebase/luci_homebase.css 2007/02/23 22:07:45 1.40
@@ -8,6 +8,60 @@
font-size: 12px;
}
+input[type=image] {
+ border: 0;
+ background: transparent;
+ padding-left: +.3333em;
+ padding-right: +.3333em;
+}
+
+input[type=image]:disabled {
+ cursor: url('cursor-disabled.png'), default;
+}
+
+input.svc_control {
+ color: transparent ! important;
+ border: none ! important;
+ outline: hidden ! important;
+ background-color: transparent ! important;
+ background-repeat: no-repeat ! important;
+ cursor: pointer;
+}
+
+.svc_control_start {
+ background-image: url(svc_start.png);
+}
+
+input.svc_control_start:hover, input.svc_control_start:active {
+ background-image: url(svc_start_active.png) ! important;
+}
+input.svc_control_start:disabled {
+ background-image: url(svc_start_disabled.png) ! important;
+ cursor: url('cursor-disabled.png'), default;
+}
+
+input.svc_control_restart {
+ background-image: url(svc_reload.png);
+}
+input.svc_control_restart:hover, input.svc_control_restart:active {
+ background-image: url(svc_reload_active.png) ! important;
+}
+input.svc_control_restart:disabled {
+ background-image: url(svc_reload_disabled.png) ! important;
+ cursor: url('cursor-disabled.png'), default;
+}
+
+input.svc_control_stop {
+ background-image: url(svc_stop.png);
+}
+input.svc_control_stop:hover, input.svc_control_stop:active {
+ background-image: url(svc_stop_active.png) ! important;
+}
+input.svc_control_stop:disabled {
+ background-image: url(svc_stop_disabled.png) ! important;
+ cursor: url('cursor-disabled.png'), default;
+}
+
input[type=checkbox], input[type=radio] {
float: left ! important;
vertical-align: middle;
/cvs/cluster/conga/luci/plone-custom/conga_ajax.js,v --> standard output
revision 1.1
--- conga/luci/plone-custom/conga_ajax.js
+++ - 2007-02-23 22:07:46.355394000 +0000
@@ -0,0 +1,29 @@
+var xmlHttp_object = false;
+
+function initiate_async_get(url, callback_function) {
+ xmlHttp_object = false;
+
+ if (!xmlHttp_object && typeof XMLHttpRequest != 'undefined') {
+ try {
+ xmlHttp_object = new XMLHttpRequest();
+ } catch (e0) {
+ try {
+ xmlHttp_object = new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ xmlHttp_object = new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e2) {
+ xmlHttp_object = false;
+ }
+ }
+ }
+ }
+
+ if (xmlHttp_object) {
+ xmlHttp_object.open("GET", url, true);
+ xmlHttp_object.onreadystatechange = callback_function;
+ xmlHttp_object.send(null);
+ } else {
+ alert("Unable to communicate with the luci server.");
+ }
+}
/cvs/cluster/conga/luci/plone-custom/svc_reload.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_reload.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_reload_active.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_reload_active.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_reload_disabled.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_reload_disabled.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_start.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_start.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_start_active.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_start_active.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_start_disabled.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_start_disabled.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_stop.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_stop.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_stop_active.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_stop_active.png and - differ
/cvs/cluster/conga/luci/plone-custom/svc_stop_disabled.png,v --> standard output
revision 1.1
Binary files /cvs/cluster/conga/luci/plone-custom/svc_stop_disabled.png and - differ
/cvs/cluster/conga/luci/site/luci/Extensions/system_adapters.py,v --> standard output
revision 1.1
--- conga/luci/site/luci/Extensions/system_adapters.py
+++ - 2007-02-23 22:07:47.286199000 +0000
@@ -0,0 +1,134 @@
+from ricci_communicator import RicciCommunicator
+from ricci_bridge import list_services, updateServices, svc_manage
+from LuciSyslog import LuciSyslog
+
+try:
+ luci_log = LuciSyslog()
+except:
+ pass
+
+def get_sys_svc_list(self, request, hostname):
+ try:
+ rc = RicciCommunicator(hostname)
+ if not rc:
+ raise Exception, 'None'
+ except Exception, e:
+ luci_log.debug_verbose('GSSL0: %s: %s' % (hostname, str(e)))
+ return []
+
+ service_list = list_services(rc)
+ if not service_list:
+ return []
+
+ svc_ret = list()
+ for i in service_list:
+ s = {}
+ try:
+ name = str(i.getAttribute('name'))
+ except:
+ name = None
+ s['name'] = name
+
+ try:
+ desc = i.getAttribute('description')
+ except:
+ desc = None
+ s['desc'] = desc
+
+ try:
+ enabled = i.getAttribute('enabled').lower()
+ if enabled == 'true':
+ s['enabled'] = True
+ else:
+ raise Exception, 'not enabled'
+ except:
+ s['enabled'] = False
+
+ try:
+ running = i.getAttribute('running').lower()
+ if running == 'true':
+ s['running'] = True
+ s['state'] = 'Running'
+ else:
+ raise Exception, 'not running'
+ except Exception, e:
+ s['running'] = False
+ s['state'] = 'Stopped'
+
+ s['starturl'] = '%s?pagetype=91&svcname=%s&systemname=%s&operation=start' % (request['URL'], s['name'], hostname)
+ s['stopurl'] = '%s?pagetype=91&svcname=%s&systemname=%s&operation=stop' % (request['URL'], s['name'], hostname)
+ s['restarturl'] = '%s?pagetype=91&svcname=%s&systemname=%s&operation=restart' % (request['URL'], s['name'], hostname)
+ svc_ret.append(s)
+
+ return svc_ret
+
+def validate_svc_update(self, request):
+ try:
+ form = request.form
+ hostname = form['systemname'].strip()
+ if not hostname:
+ raise Exception, 'blank'
+ except Exception, e:
+ return (False, {'errors': [ 'No system name was given.' ]})
+
+ try:
+ rc = RicciCommunicator(hostname)
+ if not rc:
+ raise Exception, 'unknown error'
+ except Exception, e:
+ luci_log.debug_verbose('VSU0: %s: %s' % (hostname, str(e)))
+ return (False, {'errors': [ 'Unable to connect to the ricci agent on %s: %s' % (hostname, str(e)) ]})
+
+ sys_svc_list = list()
+ sys_svc_hash = dict()
+
+ try:
+ for i in list_services(rc):
+ svc_name = str(i.getAttribute('name'))
+ sys_svc_hash[svc_name] = i
+ sys_svc_list.append(svc_name)
+ except Exception, e:
+ luci_log.debug_verbose('VSU1: %s: %s' % (hostname, str(e)))
+ return (False, {'errors': [ 'Unable to retrieve the list of services from %s' % hostname ]})
+
+ try:
+ del form['pagetype']
+ del form['systemname']
+ except:
+ pass
+
+ disable_list = filter(lambda x: not form.has_key(x) and sys_svc_hash[x].getAttribute('enabled').lower() == 'true', sys_svc_list)
+ enable_list = filter(lambda x: form.has_key(x) and sys_svc_hash[x].getAttribute('enabled').lower() != 'true', sys_svc_list)
+
+ updateServices(rc, enable_list, disable_list)
+ request.RESPONSE.redirect(request['URL'] + '?pagetype=90&systemname=' + hostname)
+
+def validate_manage_svc(self, request):
+ try:
+ hostname = request['systemname'].strip()
+ except:
+ return None
+
+ servicename = None
+ op = None
+ try:
+ servicename = request['svcname'].strip()
+ op = request['operation'].strip()
+ except:
+ pass
+
+ try:
+ rc = RicciCommunicator(hostname)
+ if not rc:
+ raise Exception, 'none'
+ except Exception, e:
+ return None
+
+ ret = None
+ try:
+ ret = svc_manage(rc, hostname, servicename, op)
+ luci_log.debug_verbose('VMC0: returning %s' % str(ret.toxml()))
+ except:
+ pass
+
+ return ret
--- conga/luci/site/luci/Extensions/cluster_adapters.py 2007/02/22 20:52:51 1.243
+++ conga/luci/site/luci/Extensions/cluster_adapters.py 2007/02/23 22:07:45 1.244
@@ -41,6 +41,7 @@
from GeneralError import GeneralError
from homebase_adapters import manageCluster, createClusterSystems, havePermCreateCluster, setNodeFlag, delNodeFlag, userAuthenticated, getStorageNode, getClusterNode, delCluster, parseHostForm
from LuciSyslog import LuciSyslog
+from system_adapters import validate_svc_update
#Policy for showing the cluster chooser menu:
#1) If there are no clusters in the ManagedClusterSystems
@@ -2453,7 +2454,8 @@
54: validateFenceEdit,
55: validateDaemonProperties,
57: deleteFenceDevice,
- 58: validateNodeFenceConfig
+ 58: validateNodeFenceConfig,
+ 90: validate_svc_update
}
def validatePost(self, request):
--- conga/luci/site/luci/Extensions/conga_constants.py 2007/02/01 20:49:08 1.36
+++ conga/luci/site/luci/Extensions/conga_constants.py 2007/02/23 22:07:45 1.37
@@ -48,6 +48,8 @@
FENCEDEV_NODE_CONFIG = '58'
CONF_EDITOR = '80'
+SYS_SERVICE_MANAGE = '90'
+SYS_SERVICE_UPDATE = '91'
#Cluster tasks
CLUSTER_STOP = '1000'
--- conga/luci/site/luci/Extensions/ricci_bridge.py 2007/02/22 20:52:51 1.58
+++ conga/luci/site/luci/Extensions/ricci_bridge.py 2007/02/23 22:07:45 1.59
@@ -486,6 +486,70 @@
ricci_xml = rc.batch_run(batch_str)
return batchAttemptResult(ricci_xml)
+def svc_manage(rc, hostname, servicename, op):
+ svc_func = None
+
+ doc = minidom.Document()
+ elem = doc.createElement('result')
+
+ if not servicename:
+ elem.setAttribute('service', 'No service name was specified.')
+ elem.setAttribute('message', 'No service name was specified.')
+ elem.setAttribute('success', '0')
+
+ if not op:
+ elem.setAttribute('operation', 'No operation was specified.')
+ elem.setAttribute('message', 'No operation was specified.')
+ elem.setAttribute('success', '0')
+
+ if not servicename or not op:
+ doc.appendChild(elem)
+ return doc
+
+ elem.setAttribute('service', servicename)
+ elem.setAttribute('operation', op)
+ elem.setAttribute('hostname', hostname)
+
+ try:
+ op = op.strip().lower()
+ if op == 'restart' or op == 'start' or op == 'stop':
+ svc_func = op
+ else:
+ raise Exception, op
+ except Exception, e:
+ elem.setAttribute('success', '0');
+ elem.setAttribute('message', 'Unknown operation')
+ doc.appendChild(elem)
+ return doc
+
+ batch_str = '<module name="service"><request API_version="1.0"><function_call name="%s"><var mutable="false" name="services" type="list_xml"><service name="%s"/></var></function_call></request></module>' % (svc_func, servicename)
+
+ ricci_xml = rc.batch_run(batch_str, async=False)
+ if not ricci_xml or not ricci_xml.firstChild:
+ luci_log.debug_verbose('SVCM0: None returned')
+ elem.setAttribute('success', '0')
+ elem.setAttribute('message', 'operation failed')
+ doc.appendChild(elem)
+ return doc
+
+ elem.setAttribute('success', '0')
+ elem.setAttribute('message', str(ricci_xml.toxml()))
+ doc.appendChild(elem)
+ return doc
+
+def list_services(rc):
+ batch_str = '<module name="service"><request API_version="1.0"><function_call name="list"><var mutable="false" name="description" type="boolean" value="true"/></function_call></request></module>'
+ ricci_xml = rc.batch_run(batch_str, async=False)
+ if not ricci_xml or not ricci_xml.firstChild:
+ luci_log.debug_verbose('LS0: None returned')
+ return None
+ try:
+ service_tags = ricci_xml.getElementsByTagName('service')
+ return service_tags
+ except Exception, e:
+ luci_log.debug_verbose('LS1: %s' % str(e))
+ return None
+
def nodeIsVirtual(rc):
batch_str = '<module name="cluster"><request API_version="1.0"><function_call name="virt_guest"/></request></module>'
next reply other threads:[~2007-02-23 22:07 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-02-23 22:07 rmccabe [this message]
-- strict thread matches above, loose matches on Subject: below --
2007-09-25 22:47 [Cluster-devel] conga/luci cluster/form-chooser cluster/form-m rmccabe
2007-03-12 4:25 rmccabe
2007-03-12 4:24 rmccabe
2007-03-12 4:22 rmccabe
2006-12-21 21:26 kupcevic
2006-12-07 17:54 rmccabe
2006-12-06 18:38 rmccabe
2006-11-10 19:44 rmccabe
2006-10-09 17:12 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=20070223220747.24209.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.