summaryrefslogtreecommitdiffstats
path: root/lib/common/attrs.c
blob: 35715df91e5cf973fd0e89aa4b7fe680390e24de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
 * Copyright 2011-2024 the Pacemaker project contributors
 *
 * The version control history for this file may have further details.
 *
 * This source code is licensed under the GNU Lesser General Public License
 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
 */

#ifndef _GNU_SOURCE
#  define _GNU_SOURCE
#endif

#include <crm_internal.h>

#include <stdio.h>

#include <crm/common/xml.h>
#include <crm/common/scheduler.h>
#include <crm/common/scheduler_internal.h>

#define OCF_RESKEY_PREFIX "OCF_RESKEY_"
#define LRM_TARGET_ENV OCF_RESKEY_PREFIX CRM_META "_" PCMK__META_ON_NODE

/*!
 * \internal
 * \brief Get the node name that should be used to set node attributes
 *
 * If given NULL, "auto", or "localhost" as an argument, check the environment
 * to detect the node name that should be used to set node attributes. (The
 * caller might not know the correct name, for example if the target is part of
 * a bundle with \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET set to
 * \c PCMK_VALUE_HOST.)
 *
 * \param[in] name  NULL, "auto" or "localhost" to check environment variables,
 *                  or anything else to return NULL
 *
 * \return Node name that should be used for node attributes based on the
 *         environment if known, otherwise NULL
 */
const char *
pcmk__node_attr_target(const char *name)
{
    if (name == NULL || pcmk__strcase_any_of(name, "auto", "localhost", NULL)) {
        char buf[128] = OCF_RESKEY_PREFIX;
        size_t offset = sizeof(OCF_RESKEY_PREFIX) - 1;
        char *target_var = crm_meta_name(PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
        char *phys_var = crm_meta_name(PCMK__META_PHYSICAL_HOST);
        const char *target = NULL;
        const char *host_physical = NULL;

        snprintf(buf + offset, sizeof(buf) - offset, "%s", target_var);
        target = getenv(buf);

        snprintf(buf + offset, sizeof(buf) - offset, "%s", phys_var);
        host_physical = getenv(buf);

        // It is important to use the name by which the scheduler knows us
        if (host_physical
            && pcmk__str_eq(target, PCMK_VALUE_HOST, pcmk__str_casei)) {
            name = host_physical;

        } else {
            const char *host_pcmk = getenv(LRM_TARGET_ENV);

            if (host_pcmk) {
                name = host_pcmk;
            }
        }
        free(target_var);
        free(phys_var);

        // TODO? Call pcmk__cluster_local_node_name() if name == NULL
        // (currently would require linkage against libcrmcluster)
        return name;
    } else {
        return NULL;
    }
}

/*!
 * \brief Return the name of the node attribute used as a promotion score
 *
 * \param[in] rsc_id  Resource ID that promotion score is for (or NULL to
 *                    check the OCF_RESOURCE_INSTANCE environment variable)
 *
 * \return Newly allocated string with the node attribute name (or NULL on
 *         error, including no ID or environment variable specified)
 * \note It is the caller's responsibility to free() the result.
 */
char *
pcmk_promotion_score_name(const char *rsc_id)
{
    if (pcmk__str_empty(rsc_id)) {
        rsc_id = getenv("OCF_RESOURCE_INSTANCE");
        if (pcmk__str_empty(rsc_id)) {
            return NULL;
        }
    }
    return crm_strdup_printf("master-%s", rsc_id);
}

/*!
 * \internal
 * \brief Get the value of a node attribute
 *
 * \param[in] node       Node to get attribute for
 * \param[in] name       Name of node attribute to get
 * \param[in] target     If this is \c PCMK_VALUE_HOST and \p node is a guest
 *                       (bundle) node, get the value from the guest's host,
 *                       otherwise get the value from \p node itself
 * \param[in] node_type  If getting the value from \p node's host, this
 *                       indicates whether to check the current or assigned host
 *
 * \return Value of \p name attribute for \p node
 */
const char *
pcmk__node_attr(const pcmk_node_t *node, const char *name, const char *target,
                enum pcmk__rsc_node node_type)
{
    const char *value = NULL;       // Attribute value to return
    const char *node_type_s = NULL; // Readable equivalent of node_type
    const pcmk_node_t *host = NULL;
    const pcmk_resource_t *container = NULL;

    if ((node == NULL) || (name == NULL)) {
        return NULL;
    }

    /* Check the node's own attributes unless this is a guest (bundle) node with
     * the container host as the attribute target.
     */
    if (!pcmk__is_guest_or_bundle_node(node)
        || !pcmk__str_eq(target, PCMK_VALUE_HOST, pcmk__str_casei)) {
        value = g_hash_table_lookup(node->details->attrs, name);
        crm_trace("%s='%s' on %s",
                  name, pcmk__s(value, ""), pcmk__node_name(node));
        return value;
    }

    /* This resource needs attributes set for the container's host instead of
     * for the container itself (useful when the container uses the host's
     * storage).
     */
    container = node->details->remote_rsc->container;

    switch (node_type) {
        case pcmk__rsc_node_assigned:
            host = container->allocated_to;
            if (host == NULL) {
                crm_trace("Skipping %s lookup for %s because "
                          "its container %s is unassigned",
                          name, pcmk__node_name(node), container->id);
                return NULL;
            }
            node_type_s = "assigned";
            break;

        case pcmk__rsc_node_current:
            if (container->running_on != NULL) {
                host = container->running_on->data;
            }
            if (host == NULL) {
                crm_trace("Skipping %s lookup for %s because "
                          "its container %s is inactive",
                          name, pcmk__node_name(node), container->id);
                return NULL;
            }
            node_type_s = "current";
            break;

        default:
            // Add support for other enum pcmk__rsc_node values if needed
            CRM_ASSERT(false);
            break;
    }

    value = g_hash_table_lookup(host->details->attrs, name);
    crm_trace("%s='%s' for %s on %s container host %s",
              name, pcmk__s(value, ""), pcmk__node_name(node), node_type_s,
              pcmk__node_name(host));
    return value;
}