1 /*
2  * Copyright 2013-2019 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <crm/msg_xml.h>
12 #include <crm/common/xml.h>
13 #include <crm/pengine/internal.h>
14 #include <glib.h>
15 
16 bool
pe__resource_is_remote_conn(const pe_resource_t * rsc,const pe_working_set_t * data_set)17 pe__resource_is_remote_conn(const pe_resource_t *rsc,
18                             const pe_working_set_t *data_set)
19 {
20     return (rsc != NULL) && rsc->is_remote_node
21            && pe__is_remote_node(pe_find_node(data_set->nodes, rsc->id));
22 }
23 
24 bool
pe__is_remote_node(const pe_node_t * node)25 pe__is_remote_node(const pe_node_t *node)
26 {
27     return (node != NULL) && (node->details->type == node_remote)
28            && ((node->details->remote_rsc == NULL)
29                || (node->details->remote_rsc->container == NULL));
30 }
31 
32 bool
pe__is_guest_node(const pe_node_t * node)33 pe__is_guest_node(const pe_node_t *node)
34 {
35     return (node != NULL) && (node->details->type == node_remote)
36            && (node->details->remote_rsc != NULL)
37            && (node->details->remote_rsc->container != NULL);
38 }
39 
40 bool
pe__is_guest_or_remote_node(const pe_node_t * node)41 pe__is_guest_or_remote_node(const pe_node_t *node)
42 {
43     return (node != NULL) && (node->details->type == node_remote);
44 }
45 
46 bool
pe__is_bundle_node(const pe_node_t * node)47 pe__is_bundle_node(const pe_node_t *node)
48 {
49     return pe__is_guest_node(node)
50            && pe_rsc_is_bundled(node->details->remote_rsc);
51 }
52 
53 /*!
54  * \internal
55  * \brief Check whether a resource creates a guest node
56  *
57  * If a given resource contains a filler resource that is a remote connection,
58  * return that filler resource (or NULL if none is found).
59  *
60  * \param[in] data_set  Working set of cluster
61  * \param[in] rsc       Resource to check
62  *
63  * \return Filler resource with remote connection, or NULL if none found
64  */
65 pe_resource_t *
pe__resource_contains_guest_node(const pe_working_set_t * data_set,const pe_resource_t * rsc)66 pe__resource_contains_guest_node(const pe_working_set_t *data_set,
67                                  const pe_resource_t *rsc)
68 {
69     if ((rsc != NULL) && (data_set != NULL)
70         && pcmk_is_set(data_set->flags, pe_flag_have_remote_nodes)) {
71 
72         for (GList *gIter = rsc->fillers; gIter != NULL; gIter = gIter->next) {
73             pe_resource_t *filler = gIter->data;
74 
75             if (filler->is_remote_node) {
76                 return filler;
77             }
78         }
79     }
80     return NULL;
81 }
82 
83 bool
xml_contains_remote_node(xmlNode * xml)84 xml_contains_remote_node(xmlNode *xml)
85 {
86     const char *value = NULL;
87 
88     if (xml == NULL) {
89         return false;
90     }
91 
92     value = crm_element_value(xml, XML_ATTR_TYPE);
93     if (!pcmk__str_eq(value, "remote", pcmk__str_casei)) {
94         return false;
95     }
96 
97     value = crm_element_value(xml, XML_AGENT_ATTR_CLASS);
98     if (!pcmk__str_eq(value, PCMK_RESOURCE_CLASS_OCF, pcmk__str_casei)) {
99         return false;
100     }
101 
102     value = crm_element_value(xml, XML_AGENT_ATTR_PROVIDER);
103     if (!pcmk__str_eq(value, "pacemaker", pcmk__str_casei)) {
104         return false;
105     }
106 
107     return true;
108 }
109 
110 /*!
111  * \internal
112  * \brief Execute a supplied function for each guest node running on a host
113  *
114  * \param[in]     data_set   Working set for cluster
115  * \param[in]     host       Host node to check
116  * \param[in]     helper     Function to call for each guest node
117  * \param[in,out] user_data  Pointer to pass to helper function
118  */
119 void
pe_foreach_guest_node(const pe_working_set_t * data_set,const pe_node_t * host,void (* helper)(const pe_node_t *,void *),void * user_data)120 pe_foreach_guest_node(const pe_working_set_t *data_set, const pe_node_t *host,
121                       void (*helper)(const pe_node_t*, void*), void *user_data)
122 {
123     GList *iter;
124 
125     CRM_CHECK(data_set && host && host->details && helper, return);
126     if (!pcmk_is_set(data_set->flags, pe_flag_have_remote_nodes)) {
127         return;
128     }
129     for (iter = host->details->running_rsc; iter != NULL; iter = iter->next) {
130         pe_resource_t *rsc = (pe_resource_t *) iter->data;
131 
132         if (rsc->is_remote_node && (rsc->container != NULL)) {
133             pe_node_t *guest_node = pe_find_node(data_set->nodes, rsc->id);
134 
135             if (guest_node) {
136                 (*helper)(guest_node, user_data);
137             }
138         }
139     }
140 }
141 
142 /*!
143  * \internal
144  * \brief Create CIB XML for an implicit remote connection
145  *
146  * \param[in] parent           If not NULL, use as parent XML element
147  * \param[in] uname            Name of Pacemaker Remote node
148  * \param[in] container        If not NULL, use this as connection container
149  * \param[in] migrateable      If not NULL, use as allow-migrate value
150  * \param[in] is_managed       If not NULL, use as is-managed value
151  * \param[in] start_timeout    If not NULL, use as remote connect timeout
152  * \param[in] server           If not NULL, use as remote server value
153  * \param[in] port             If not NULL, use as remote port value
154  */
155 xmlNode *
pe_create_remote_xml(xmlNode * parent,const char * uname,const char * container_id,const char * migrateable,const char * is_managed,const char * start_timeout,const char * server,const char * port)156 pe_create_remote_xml(xmlNode *parent, const char *uname,
157                      const char *container_id, const char *migrateable,
158                      const char *is_managed, const char *start_timeout,
159                      const char *server, const char *port)
160 {
161     xmlNode *remote;
162     xmlNode *xml_sub;
163 
164     remote = create_xml_node(parent, XML_CIB_TAG_RESOURCE);
165 
166     // Add identity
167     crm_xml_add(remote, XML_ATTR_ID, uname);
168     crm_xml_add(remote, XML_AGENT_ATTR_CLASS, PCMK_RESOURCE_CLASS_OCF);
169     crm_xml_add(remote, XML_AGENT_ATTR_PROVIDER, "pacemaker");
170     crm_xml_add(remote, XML_ATTR_TYPE, "remote");
171 
172     // Add meta-attributes
173     xml_sub = create_xml_node(remote, XML_TAG_META_SETS);
174     crm_xml_set_id(xml_sub, "%s-%s", uname, XML_TAG_META_SETS);
175     crm_create_nvpair_xml(xml_sub, NULL,
176                           XML_RSC_ATTR_INTERNAL_RSC, XML_BOOLEAN_TRUE);
177     if (container_id) {
178         crm_create_nvpair_xml(xml_sub, NULL,
179                               XML_RSC_ATTR_CONTAINER, container_id);
180     }
181     if (migrateable) {
182         crm_create_nvpair_xml(xml_sub, NULL,
183                               XML_OP_ATTR_ALLOW_MIGRATE, migrateable);
184     }
185     if (is_managed) {
186         crm_create_nvpair_xml(xml_sub, NULL, XML_RSC_ATTR_MANAGED, is_managed);
187     }
188 
189     // Add instance attributes
190     if (port || server) {
191         xml_sub = create_xml_node(remote, XML_TAG_ATTR_SETS);
192         crm_xml_set_id(xml_sub, "%s-%s", uname, XML_TAG_ATTR_SETS);
193         if (server) {
194             crm_create_nvpair_xml(xml_sub, NULL, XML_RSC_ATTR_REMOTE_RA_ADDR,
195                                   server);
196         }
197         if (port) {
198             crm_create_nvpair_xml(xml_sub, NULL, "port", port);
199         }
200     }
201 
202     // Add operations
203     xml_sub = create_xml_node(remote, "operations");
204     crm_create_op_xml(xml_sub, uname, "monitor", "30s", "30s");
205     if (start_timeout) {
206         crm_create_op_xml(xml_sub, uname, "start", "0", start_timeout);
207     }
208     return remote;
209 }
210 
211 // History entry to be checked for fail count clearing
212 struct check_op {
213     xmlNode *rsc_op;    // History entry XML
214     pe_resource_t *rsc; // Known resource corresponding to history entry
215     pe_node_t *node;    // Known node corresponding to history entry
216     enum pe_check_parameters check_type; // What needs checking
217 };
218 
219 void
pe__add_param_check(xmlNode * rsc_op,pe_resource_t * rsc,pe_node_t * node,enum pe_check_parameters flag,pe_working_set_t * data_set)220 pe__add_param_check(xmlNode *rsc_op, pe_resource_t *rsc, pe_node_t *node,
221                    enum pe_check_parameters flag, pe_working_set_t *data_set)
222 {
223     struct check_op *check_op = NULL;
224 
225     CRM_CHECK(data_set && rsc_op && rsc && node, return);
226 
227     check_op = calloc(1, sizeof(struct check_op));
228     CRM_ASSERT(check_op != NULL);
229 
230     crm_trace("Deferring checks of %s until after allocation", ID(rsc_op));
231     check_op->rsc_op = rsc_op;
232     check_op->rsc = rsc;
233     check_op->node = node;
234     check_op->check_type = flag;
235     data_set->param_check = g_list_prepend(data_set->param_check, check_op);
236 }
237 
238 /*!
239  * \internal
240  * \brief Call a function for each action to be checked for addr substitution
241  *
242  * \param[in] data_set  Working set for cluster
243  * \param[in] cb        Function to be called
244  */
245 void
pe__foreach_param_check(pe_working_set_t * data_set,void (* cb)(pe_resource_t *,pe_node_t *,xmlNode *,enum pe_check_parameters,pe_working_set_t *))246 pe__foreach_param_check(pe_working_set_t *data_set,
247                        void (*cb)(pe_resource_t*, pe_node_t*, xmlNode*,
248                                   enum pe_check_parameters, pe_working_set_t*))
249 {
250     CRM_CHECK(data_set && cb, return);
251 
252     for (GList *item = data_set->param_check; item != NULL; item = item->next) {
253         struct check_op *check_op = item->data;
254 
255         cb(check_op->rsc, check_op->node, check_op->rsc_op,
256            check_op->check_type, data_set);
257     }
258 }
259 
260 void
pe__free_param_checks(pe_working_set_t * data_set)261 pe__free_param_checks(pe_working_set_t *data_set)
262 {
263     if (data_set && data_set->param_check) {
264         g_list_free_full(data_set->param_check, free);
265         data_set->param_check = NULL;
266     }
267 }
268