Skip to content

Commit bba2584

Browse files
committed
allow virtual modules for load-balance and redundant-load-balance
so that they can have second names, and also a key.
1 parent 50f42f3 commit bba2584

File tree

5 files changed

+101
-22
lines changed

5 files changed

+101
-22
lines changed

doc/antora/modules/reference/pages/unlang/load-balance.adoc

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,44 @@
33
.Syntax
44
[source,unlang]
55
----
6-
load-balance [ <key> ] {
6+
load-balance [ <name> ] [ <key> ] {
77
[ statements ]
88
}
99
----
1010

1111
The `load-balance` section is similar to the `redundant` section
12-
except that only one module in the subsection is ever called.
12+
except that only one statement in the subsection is ever called.
1313

1414
In general, the
1515
xref:unlang/redundant-load-balance.adoc[redundant-load-balance] statement is
1616
more useful than this one.
1717

18+
<name>:: The instance name of the `load-balance` section. This name
19+
is only used when the `load-balance` keyword is used inside of a
20+
xref:unlang/load-balance.adoc#virtual_modules[virtual module]. In
21+
other situations, it is not permitted.
22+
+
23+
The name must be a fixed string.
24+
1825
<key>:: An attribute reference or expansion which will be hashed in
19-
order to select the statement to execute.
26+
order to select the statement to execute. Fixed strings or data are
27+
not permitted, as that would cause load-balancing to always select the
28+
same statement.
2029
+
2130
The hash will be used to pick a particular statement within the
2231
`load-balance` section. This "keyed" load-balance can be used to
2332
deterministically shard requests across multiple modules.
2433
+
25-
When the `<key>` field is omitted, the module is chosen randomly, in a
26-
"load balanced" manner.
34+
When the `<key>` field is omitted, or the key is not found, the
35+
a random statement is chosen instead.
2736

2837
[ statements ]:: One or more `unlang` commands. Only one of the
2938
statements is executed.
39+
+
40+
41+
The statements should all be different. Attribute assignments should
42+
be placed inside of a xref:unlang/group.adoc[group] section, otherwise
43+
an error will be generated.
3044

3145
.Examples
3246

@@ -38,7 +52,8 @@ load-balance User-Name {
3852
}
3953
----
4054

41-
== load-balance Sections as Modules
55+
[#virtual_modules]
56+
== Virtual Modules
4257

4358
It can be useful to use the same `load-balance` section in multiple
4459
places. Instead of copying the same text multiple times, a

src/lib/server/cf_file.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2633,6 +2633,40 @@ static int parse_input(cf_stack_t *stack)
26332633

26342634
fr_skip_whitespace(ptr);
26352635

2636+
/*
2637+
* load-balance and redundant-load-balance MUST have a static module name, and MAY have
2638+
* an additional load-balance keyword.
2639+
*/
2640+
if ((parent->unlang == CF_UNLANG_MODULES) && (*ptr != '{') &&
2641+
((strcmp(buff[1], "load-balance") == 0) ||
2642+
(strcmp(buff[1], "redundant-load-balance") == 0))) {
2643+
/*
2644+
* The third name could be an attribute name, xlat expansion, etc.
2645+
*/
2646+
if (cf_get_token(parent, &ptr, &value_token, buff[3], stack->bufsize,
2647+
frame->filename, frame->lineno) < 0) {
2648+
return -1;
2649+
}
2650+
2651+
fr_skip_whitespace(ptr);
2652+
2653+
if (*ptr != '{') {
2654+
return parse_error(stack, ptr, "Expected '{'");
2655+
}
2656+
2657+
ptr++;
2658+
2659+
css = cf_section_alloc(parent, parent, buff[1], buff[2]);
2660+
if (!css) goto oom;
2661+
2662+
css->argc = 1;
2663+
css->argv = talloc_array(css, char const *, 1);
2664+
css->argv[0] = talloc_typed_strdup(css->argv, buff[3]);
2665+
css->argv_quote = talloc_array(css, fr_token_t, 1);
2666+
css->argv_quote[0] = value_token;
2667+
goto setup_section;
2668+
}
2669+
26362670
if (*ptr != '{') {
26372671
return parse_error(stack, ptr, "Missing '{' for configuration section");
26382672
}
@@ -2800,6 +2834,7 @@ static int parse_input(cf_stack_t *stack)
28002834
return -1;
28012835
}
28022836

2837+
setup_section:
28032838
cf_filename_set(css, frame->filename);
28042839
cf_lineno_set(css, frame->lineno);
28052840
css->name2_quote = name2_token;

src/lib/unlang/load_balance.c

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,14 @@ static unlang_action_t unlang_load_balance(unlang_result_t *p_result, request_t
139139
char buffer[1024];
140140

141141
/*
142-
* Integer data types let the admin
143-
* select which frame is being used.
142+
* Hash the attribute value to select the statement which will be used.
144143
*/
145144
if (tmpl_is_attr(gext->vpt)) {
146145
fr_pair_t *vp;
147146

148147
slen = tmpl_find_vp(&vp, request, gext->vpt);
149148
if (slen < 0) {
150-
REDEBUG("Failed finding attribute %s", gext->vpt->name);
149+
REDEBUG("Failed finding attribute %s - choosing random statement", gext->vpt->name);
151150
goto randomly_choose;
152151
}
153152

@@ -159,11 +158,13 @@ static unlang_action_t unlang_load_balance(unlang_result_t *p_result, request_t
159158
uint8_t *octets = NULL;
160159

161160
/*
162-
* If the input is an IP address, prefix, etc., we don't need to convert it to a
163-
* string. We can just hash the raw data directly.
161+
* Get the raw data, and then hash the data.
164162
*/
165163
slen = tmpl_expand(&octets, buffer, sizeof(buffer), request, gext->vpt);
166-
if (slen <= 0) goto randomly_choose;
164+
if (slen <= 0) {
165+
REDEBUG("Failed expanding %s - choosing random statement", gext->vpt->name);
166+
goto randomly_choose;
167+
}
167168

168169
hash = fr_hash(octets, slen);
169170

@@ -260,24 +261,27 @@ static unlang_t *compile_load_balance_subsection(unlang_t *parent, unlang_compil
260261
* Allow for keyed load-balance / redundant-load-balance sections.
261262
*/
262263
name2 = cf_section_name2(cs);
263-
264-
/*
265-
* Inside of the "modules" section, it's a virtual
266-
* module. The name is a module name, not a key.
267-
*/
268-
if (name2) {
269-
if (strcmp(cf_section_name1(cf_item_to_section(cf_parent(cs))), "modules") == 0) name2 = NULL;
270-
}
271-
272264
if (name2) {
273265
fr_token_t quote;
274266
ssize_t slen;
275267

268+
/*
269+
* Inside of the "modules" section, it's a virtual module. The key is the third argument, and
270+
* the "name2" is the module name, which we ignore here.
271+
*/
272+
if (strcmp(cf_section_name1(cf_item_to_section(cf_parent(cs))), "modules") == 0) {
273+
name2 = cf_section_argv(cs, 0);
274+
fr_assert(name2);
275+
quote = cf_section_argv_quote(cs, 0);
276+
277+
} else {
278+
quote = cf_section_name2_quote(cs);
279+
}
280+
276281
/*
277282
* Create the template. All attributes and xlats are
278283
* defined by now.
279284
*/
280-
quote = cf_section_name2_quote(cs);
281285
gext = unlang_group_to_load_balance(g);
282286
slen = tmpl_afrom_substr(gext, &gext->vpt,
283287
&FR_SBUFF_IN_STR(name2),
@@ -300,6 +304,8 @@ static unlang_t *compile_load_balance_subsection(unlang_t *parent, unlang_compil
300304
return NULL;
301305
}
302306

307+
ERROR("SHIT %s %d", gext->vpt->name, gext->vpt->type);
308+
303309
switch (gext->vpt->type) {
304310
default:
305311
cf_log_err(cs, "Invalid type in '%s': data will not result in a load-balance key", name2);

src/tests/keywords/radius.conf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,24 @@ modules {
105105
#
106106
# Just check that this can be referred to as "virtual_instantiate.post-auth"
107107
#
108+
108109
load-balance virtual_instantiate {
109110
ok
110111
updated
111112
}
112113

114+
#
115+
# Name is "keyed-virtual". The key is Calling-Station-Id.
116+
#
117+
load-balance keyed-virtual Calling-Station-Id {
118+
group {
119+
control.Filter-Id := 1
120+
}
121+
group {
122+
control.Filter-Id := 2
123+
}
124+
}
125+
113126
test test1 {
114127
}
115128

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#
2+
# Just to see if this compiles and runs
3+
#
4+
keyed-virtual
5+
6+
if (!control.Filter-Id) {
7+
test_fail
8+
}
9+
10+
success

0 commit comments

Comments
 (0)