From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 2240 invoked by alias); 26 Jun 2008 16:25:31 -0000 Received: (qmail 2189 invoked by uid 9452); 26 Jun 2008 16:25:28 -0000 Date: Thu, 26 Jun 2008 16:25:00 -0000 Message-ID: <20080626162528.2173.qmail@sourceware.org> From: ccaulfield@sourceware.org To: cluster-cvs@sources.redhat.com, cluster-devel@redhat.com Subject: Cluster Project branch, master, updated. cluster-2.99.05-23-gee712cf X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: a006cbd31804652e23ab5dde423b2b2e7fa03f76 X-Git-Newrev: ee712cf5e8d6213c509d1dc536dac70332ae63fe Mailing-List: contact cluster-cvs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: cluster-cvs-owner@sourceware.org X-SW-Source: 2008-q2/txt/msg00552.txt.bz2 This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Cluster Project". http://sources.redhat.com/git/gitweb.cgi?p=cluster.git;a=commitdiff;h=ee712cf5e8d6213c509d1dc536dac70332ae63fe The branch, master has been updated via ee712cf5e8d6213c509d1dc536dac70332ae63fe (commit) from a006cbd31804652e23ab5dde423b2b2e7fa03f76 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit ee712cf5e8d6213c509d1dc536dac70332ae63fe Author: Christine Caulfield Date: Thu Jun 26 17:22:23 2008 +0100 [CONFIG] Add ldap configurator This is an openais configuration plugin to read the cluster config from an LDAP server. A schema file is included that provides just enough information to get a cluster running, more will follow. There is also an example ldif file to show how to load the information into the database. The defaults are slightly odd at the moment, I'll fix those as it develops and document how to override them. In the mean time see the top of the source code. Signed-off-by: Christine Caulfield ----------------------------------------------------------------------- Summary of changes: config/plugins/Makefile | 2 +- config/plugins/ldap/99cluster.ldif | 120 ++++++++++++++ config/plugins/{xml => ldap}/Makefile | 8 +- config/plugins/ldap/configldap.c | 284 +++++++++++++++++++++++++++++++++ config/plugins/ldap/example.ldif | 89 ++++++++++ 5 files changed, 497 insertions(+), 6 deletions(-) create mode 100644 config/plugins/ldap/99cluster.ldif copy config/plugins/{xml => ldap}/Makefile (71%) create mode 100644 config/plugins/ldap/configldap.c create mode 100644 config/plugins/ldap/example.ldif diff --git a/config/plugins/Makefile b/config/plugins/Makefile index 1305555..a067c80 100644 --- a/config/plugins/Makefile +++ b/config/plugins/Makefile @@ -1,4 +1,4 @@ include ../../make/defines.mk include $(OBJDIR)/make/passthrough.mk -SUBDIRS=xml +SUBDIRS=xml ldap diff --git a/config/plugins/ldap/99cluster.ldif b/config/plugins/ldap/99cluster.ldif new file mode 100644 index 0000000..2f0091d --- /dev/null +++ b/config/plugins/ldap/99cluster.ldif @@ -0,0 +1,120 @@ +# Schema for Red Hat cluster suite LDAP configuration +# 2008, Christine Caulfield ccaulfie@redhat.com +# +# This schema is incomplete, and probably always will be +# + +dn: cn=schema +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.1 NAME 'rhcsConfig-version' + DESC 'An integer describing the configuration version' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.2 NAME 'rhcsNodeid' + DESC 'An integer describing the node ID number' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.3 NAME 'rhcsCluster-id' + DESC 'An integer describing the cluster ID number' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.4 NAME 'rhcsVotes' + DESC 'An integer describing the number of votes a node has' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.5 NAME 'rhcsTwo-node' + DESC 'set to 1 for two_node mode' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.6 NAME 'rhcsExpected-votes' + DESC 'An integer describing the number of votes expected for the whole cluster' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.7 NAME 'rhcsMax-queued' + DESC 'An integer describing the maximum number of outstanding client requests to cman' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.8 NAME 'rhcsToken' + DESC 'An integer describing the totem token timeout' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.9 NAME 'rhcsAgent' + DESC 'The fencing agent to use' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.10 NAME 'rhcsUsername' + DESC 'Username to log into the fencing agent' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.11 NAME 'rhcsPassword' + DESC 'Password to log into the fencing agent' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE + ) +attributeTypes: ( + 1.3.6.1.4.1.2312.8.1.1.12 NAME 'rhcsIpaddr' + DESC 'IP Address the fencing agent' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE + ) +objectClasses: ( + 1.3.6.1.4.1.2312.8.1.2.1 NAME 'rhcsCluster' SUP top STRUCTURAL + DESC 'Cluster top-level entry' + MUST ( cn $ name $ rhcsConfig-version ) + ) +objectClasses: ( + 1.3.6.1.4.1.2312.8.1.2.2 NAME 'rhcsNode' SUP top STRUCTURAL + DESC 'Cluster node entry' + MUST ( name $ rhcsNodeid ) + MAY rhcsVotes + ) +objectClasses: ( + 1.3.6.1.4.1.2312.8.1.2.3 NAME 'rhcsCman' SUP top STRUCTURAL + DESC 'Cluster node entry' + MUST ( cn $ name $ rhcsNodeid ) + MAY ( rhcsCluster-id $ rhcsTwo-node $ rhcsExpected-votes $ rhcsMax-queued ) + ) +objectClasses: ( + 1.3.6.1.4.1.2312.8.1.2.4 NAME 'rhcsTotem' SUP top STRUCTURAL + DESC 'Totem options' + MUST ( cn ) + MAY ( rhcsToken ) + ) +objectClasses: ( + 1.3.6.1.4.1.2312.8.1.2.5 NAME 'rhcsFencedevice' SUP top STRUCTURAL + DESC 'A Fence device' + MUST ( name $ rhcsAgent ) + MAY ( rhcsIpaddr $ rhcsUsername $ rhcsPassword ) + ) diff --git a/config/plugins/xml/Makefile b/config/plugins/ldap/Makefile similarity index 71% copy from config/plugins/xml/Makefile copy to config/plugins/ldap/Makefile index 9274336..efd764c 100644 --- a/config/plugins/xml/Makefile +++ b/config/plugins/ldap/Makefile @@ -1,4 +1,4 @@ -TARGET= config_xml.lcrso +TARGET= config_ldap.lcrso LCRSOT=$(TARGET) @@ -11,13 +11,11 @@ include $(OBJDIR)/make/install.mk include $(OBJDIR)/make/uninstall.mk CFLAGS += -fPIC -CFLAGS += `xml2-config --cflags` -CFLAGS += -I${cmanincdir}/../daemon CFLAGS += -I${incdir} -LDFLAGS += `xml2-config --libs` +LDFLAGS += -lldap -OBJS= config.o +OBJS= configldap.o ${TARGET}: ${OBJS} $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS) diff --git a/config/plugins/ldap/configldap.c b/config/plugins/ldap/configldap.c new file mode 100644 index 0000000..9e718b1 --- /dev/null +++ b/config/plugins/ldap/configldap.c @@ -0,0 +1,284 @@ +/****************************************************************************** +******************************************************************************* +** +** Copyright (C) 2008 Red Hat, Inc. All rights reserved. +** +** This copyrighted material is made available to anyone wishing to use, +** modify, copy, or redistribute it subject to the terms and conditions +** of the GNU General Public License v.2. +** +******************************************************************************* +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +// CC: temp until I tame SASL ... is this necessary? +#define LDAP_DEPRECATED 1 +#include + +/* openais headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_LDAP_URL "ldap:///" +#define DEFAULT_LDAP_BASEDN "cn=cluster,dc=chrissie,dc=net" + +static int ldap_readconfig(struct objdb_iface_ver0 *objdb, char **error_string); +static int init_config(struct objdb_iface_ver0 *objdb, char *error_string); +static char error_reason[1024]; + +/* + * Exports the interface for the service + */ + +static struct config_iface_ver0 ldapconfig_iface_ver0 = { + .config_readconfig = ldap_readconfig +}; + +static struct lcr_iface ifaces_ver0[2] = { + { + .name = "ldapconfig", + .version = 0, + .versions_replace = 0, + .versions_replace_count = 0, + .dependencies = 0, + .dependency_count = 0, + .constructor = NULL, + .destructor = NULL, + .interfaces = NULL, + } +}; + +static struct lcr_comp ldap_comp_ver0 = { + .iface_count = 1, + .ifaces = ifaces_ver0, +}; + + + +__attribute__ ((constructor)) static void ldap_comp_register(void) { + lcr_interfaces_set(&ifaces_ver0[0], &ldapconfig_iface_ver0); + lcr_component_register(&ldap_comp_ver0); +}; + +static int ldap_readconfig(struct objdb_iface_ver0 *objdb, char **error_string) +{ + int ret; + + /* Read config tree from LDAP */ + if (!(ret = init_config(objdb, error_reason))) + sprintf(error_reason, "%s", "Successfully read config from LDAP\n"); + + *error_string = error_reason; + + return ret; +} + +/* Specify the search criteria here. */ +static char *ldap_url = DEFAULT_LDAP_URL; +static char *ldap_basedn = DEFAULT_LDAP_BASEDN; + +/* + * Convert hyphens to underscores in all attribute names + */ +static void convert_underscores(char *s, int len) +{ + int j; + + for (j=0; j < len; j++) { + if (s[j] == '-') + s[j] = '_'; + } +} + +static void convert_dn_underscores(LDAPDN dn) +{ + int i=0; + + while (dn[i]) { + convert_underscores(dn[i][0][0].la_attr.bv_val, dn[i][0][0].la_attr.bv_len); + i++; + } +} + +/* + * Return the parent object of a DN. + * Actually, this returns the LAST parent with that name. which should (!) be correct. + */ +static unsigned int find_parent(struct objdb_iface_ver0 *objdb, LDAPDN dn, int startdn) +{ + int i=startdn; + int gotstart=0; + int start=0, end=startdn; + unsigned int parent_handle = OBJECT_PARENT_HANDLE; + unsigned int object_handle=0; + +// fprintf(stderr, "CC: find parent: startdn=%d\n", startdn); + + /* + * Find the start and end positions first. + * start is where the 'cluster' entry is. + * end is the end of the list + */ + do { +// fprintf(stderr, "CC: %d: seen %s\n", i,dn[i][0][0].la_value.bv_val); + if (!gotstart && dn[i][0][0].la_value.bv_len == 7 && + !strncmp("cluster", dn[i][0][0].la_value.bv_val, 7)) { + gotstart = 1; + start = i; + } + i++; + } while (dn[i]); + if (start <= 0) + return parent_handle; + +// fprintf(stderr, "CC: find parent: start=%d, end=%d\n", start, end); + + for (i=start; i>=end; i--) { + objdb->object_find_reset(parent_handle); +// fprintf(stderr, "CC: %d: looking for %s\n", i,dn[i][0][0].la_value.bv_val); + if (!objdb->object_find(parent_handle, + dn[i][0][0].la_value.bv_val, dn[i][0][0].la_value.bv_len, + &object_handle)) { + parent_handle = object_handle; + } + } + return object_handle; +} + +/* The real work starts here */ +static int init_config(struct objdb_iface_ver0 *objdb, char *error_string) +{ + LDAP *ld; + LDAPMessage *result, *e; + char *dn; + int version, rc; + unsigned int parent_handle = OBJECT_PARENT_HANDLE; + unsigned int object_handle; + + if (getenv("LDAP_URL")) + ldap_url = getenv("LDAP_URL"); + if (getenv("LDAP_BASEDN")) + ldap_basedn = getenv("LDAP_BASEDN"); + + /* Connect to the LDAP server */ + if (ldap_initialize(&ld, ldap_url)) { + perror("ldap_initialize"); + return -1; + } + version = LDAP_VERSION3; + ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); + + /* + * CC: Do I need to use sasl ?! + */ + rc = ldap_simple_bind_s(ld, getenv("LDAP_BINDDN"), getenv("LDAP_BINDPWD")); + if (rc != LDAP_SUCCESS) { + fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc)); + return -1; + } + + /* Search the whole tree from the base DN provided */ + rc = ldap_search_ext_s(ld, ldap_basedn, LDAP_SCOPE_SUBTREE, "(objectClass=*)", NULL, 0, + NULL, NULL, NULL, 0, &result); + if (rc != LDAP_SUCCESS) { + fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc)); + return 1; + } + for (e = ldap_first_entry(ld, result); e != NULL; + e = ldap_next_entry(ld, e)) { + if ((dn = ldap_get_dn(ld, e)) != NULL) { + char *attr; + BerElement *attr_ber; + LDAPDN parsed_dn; + + /* Make it parsable so we can discern the hierarchy */ + if (ldap_str2dn(dn, &parsed_dn, LDAP_DN_PEDANTIC)) { + strcpy(error_reason, strerror(errno)); + return -1; + } + + /* + * LDAP doesn't allow underscores in dn names so we replace hypens with + * underscores so we can have thing like config_version, appear as + * config-version in ldap + */ + convert_dn_underscores(parsed_dn); + + /* Create a new object if the top-level is NOT name= */ +// printf("CC: dn: %s\n", dn); + if (strncmp(parsed_dn[0][0][0].la_attr.bv_val, "name", 4)) { + parent_handle = find_parent(objdb, parsed_dn, 0); + + objdb->object_create(parent_handle, &object_handle, parsed_dn[0][0][0].la_value.bv_val, + parsed_dn[0][0][0].la_value.bv_len); + } + else { + parent_handle = find_parent(objdb, parsed_dn, 2); + /* Create a new object with the same name as the current one */ + objdb->object_create(parent_handle, &object_handle, parsed_dn[1][0][0].la_value.bv_val, + parsed_dn[1][0][0].la_value.bv_len); + + } + + /* Finished with the text representation */ + ldap_memfree(dn); + + /* Store the attributes as keys */ + attr = ldap_first_attribute(ld, e, &attr_ber); + while (attr) { + int i; + struct berval **val_ber; + + val_ber = ldap_get_values_len(ld, e, attr); + i=0; + while (val_ber[i]) { + /* + * If the attribute starts "rhcs" then remove that bit + * and make the first letter lower case so it matches the + * cluster.conf entry. + * so, after the above underscore change too: + * eg 'rhcsConfig-version' becomes 'config_version'. magic! + */ + if (strncmp(attr, "rhcs", 4) == 0) { + memmove(attr, attr+4, strlen(attr+4)+1); + attr[0] |= 0x60; + } + convert_underscores(attr, strlen(attr)); + + /* + * Add a key - but ignore "objectClass" & "cn" attributes + * as they don't provide anything we can use + */ + if (strcmp("objectClass", attr) && + strcmp("cn", attr)) + objdb->object_key_create(object_handle, attr, strlen(attr), + val_ber[i]->bv_val, + val_ber[i]->bv_len+1); + i++; + } + ldap_memfree(attr); + attr = ldap_next_attribute(ld, e, attr_ber); + ldap_value_free_len(val_ber); + } + ldap_memfree(attr); + ber_free(attr_ber, 0); + } + } + ldap_msgfree(result); + + ldap_unbind(ld); + return 0; +} diff --git a/config/plugins/ldap/example.ldif b/config/plugins/ldap/example.ldif new file mode 100644 index 0000000..e7cb99d --- /dev/null +++ b/config/plugins/ldap/example.ldif @@ -0,0 +1,89 @@ + +# Example cluster LDIF file +dn: cn=cluster,dc=chrissie,dc=net +cn: cluster +objectClass: rhcsCluster +name: cc_cluster +rhcsConfig-version: 1 + +# Some cman parameters +dn: cn=cman,cn=cluster,dc=chrissie,dc=net +cn: cman +objectClass: rhcsCman +rhcsCluster-id: 444 + +# Some totem parameters +dn: cn=totem,cn=cluster,dc=chrissie,dc=net +cn: totem +objectClass: rhcsTotem +rhcsToken: 21000 + +# Define nodes +dn: cn=clusternodes,cn=cluster,dc=chrissie,dc=net +cn: clusternodes +objectClass: nsContainer + + +dn: cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +cn: clusternode +objectClass: nsContainer + + +dn: name=jeltz,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +objectClass: rhcsNode +name: jeltz +rhcsNodeid: 1 +rhcsVotes: 2 + +# Define a fence agent for this node ...! +dn: cn=fence,name=jeltz,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +cn: fence +objectclass: nsContainer + +dn: cn=method,cn=fence,name=jeltz,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +cn: method +objectclass: nsContainer + +dn: name=apc,cn=method,cn=fence,name=jeltz,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +name: apc +objectclass: nsContainer + +dn: cn=device,name=apc,cn=method,cn=fence,name=jeltz,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +cn: device +objectclass: nsContainer + +dn: name=myapc,cn=device,name=apc,cn=method,cn=fence,name=jeltz,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +name: myapc +objectclass: nsContainer +port=4 + +dn: name=arthur,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +objectClass: rhcsNode +name: arthur +rhcsNodeid: 10 +rhcsVotes: 1 + +dn: name=ford,cn=clusternode,cn=clusternodes,cn=cluster,dc=chrissie,dc=net +objectClass: rhcsNode +name: ford +rhcsNodeid: 32 +rhcsVotes: 1 + +# Fence agent + +dn: cn=fencedevices,cn=cluster,dc=chrissie,dc=net +cn: fencedevices +objectClass: nsContainer + +dn: cn=fencedevice,cn=fencedevices,cn=cluster,dc=chrissie,dc=net +cn: fencedevice +objectClass: nsContainer + + +dn: name=myapc,cn=fencedevice,cn=fencedevices,cn=cluster,dc=chrissie,dc=net +objectClass: rhcsFencedevice +name: myapc +rhcsAgent: fence_apc +rhcsIpaddr: myapc.chrissie.net +rhcsUsername: apc +rhcsPassword: apc \ No newline at end of file hooks/post-receive -- Cluster Project