1<?php
2
3# This file is a part of RackTables, a datacenter and server room management
4# framework. See accompanying file "COPYING" for the full copyright and
5# licensing information.
6
7$vs_proto = array (
8	'TCP' => 'TCP',
9	'UDP' => 'UDP',
10	'MARK' => 'MARK',
11);
12
13// *********************  Config-generating functions  *********************
14
15// you may override the class name to make using your own triplet class
16$triplet_class = 'SLBTriplet';
17$parser_class = 'MacroParser';
18
19class SLBTriplet
20{
21	public $lb;
22	public $vs;
23	public $rs;
24	public $slb;
25	public $display_cells;
26
27	public function __construct ($lb_id, $vs_id, $rs_id, $db_row = NULL)
28	{
29		$this->lb = spotEntity ('object', $lb_id);
30		$this->vs = spotEntity ('ipv4vs', $vs_id);
31		$this->rs = spotEntity ('ipv4rspool', $rs_id);
32		$this->display_cells = array ('lb', 'vs', 'rs');
33		if (isset ($db_row))
34			$this->slb = $db_row;
35		else
36		{
37			$result = usePreparedSelectBlade
38			(
39				"SELECT prio, vsconfig, rsconfig FROM IPv4LB WHERE object_id = ? AND vs_id = ? AND rspool_id = ?",
40				array ($lb_id, $vs_id, $rs_id)
41			);
42			if ($row = $result->fetch (PDO::FETCH_ASSOC))
43				$this->slb = $row;
44			else
45				throw new RackTablesError ("SLB triplet not found in the DB");
46		}
47	}
48
49	static public function getTriplets ($cell)
50	{
51		if (isset ($cell['ip_bin']) && isset ($cell['vslist']))
52			// cell is IPAddress
53			return self::getTripletsByIP ($cell['ip_bin']);
54		$ret = array();
55		switch ($cell['realm'])
56		{
57			case 'object':
58				$db_field = 'object_id';
59				$order_fields = 'vs_id';
60				$display_cells = array('vs', 'rs');
61				break;
62			case 'ipv4vs':
63				$db_field = 'vs_id';
64				$order_fields = 'rspool_id';
65				$display_cells = array('rs', 'lb');
66				break;
67			case 'ipv4rspool':
68				$db_field = 'rspool_id';
69				$order_fields = 'vs_id';
70				$display_cells = array('vs', 'lb');
71				break;
72			default:
73				throw new InvalidArgException ('realm', $cell['realm']);
74		}
75		$result = usePreparedSelectBlade
76		(
77			'SELECT object_id, rspool_id, vs_id, prio, vsconfig, rsconfig FROM IPv4LB ' .
78			"WHERE `$db_field` = ? ORDER BY $order_fields",
79			array ($cell['id'])
80		);
81		$rows = $result->fetchAll (PDO::FETCH_ASSOC);
82		unset ($result);
83		global $triplet_class;
84		foreach ($rows as $row)
85		{
86			$row['vsconfig'] = dos2unix ($row['vsconfig']);
87			$row['rsconfig'] = dos2unix ($row['rsconfig']);
88			$triplet = new $triplet_class ($row['object_id'], $row['vs_id'], $row['rspool_id'], $row);
89			$triplet->display_cells = $display_cells;
90			$ret[] = $triplet;
91		}
92		return $ret;
93	}
94
95	static public function getTripletsByIP ($ip_bin)
96	{
97		$ret = array();
98		$result = usePreparedSelectBlade ("
99SELECT DISTINCT IPv4LB.object_id, IPv4LB.rspool_id, IPv4LB.vs_id, IPv4LB.prio, IPv4LB.vsconfig, IPv4LB.rsconfig
100FROM
101	IPv4LB INNER JOIN IPv4VS ON IPv4VS.id = IPv4LB.vs_id
102	LEFT JOIN IPv4RS USING (rspool_id)
103WHERE
104	rsip = ? OR vip = ?
105ORDER BY
106	vs_id
107	", array ($ip_bin, $ip_bin)
108		);
109		$rows = $result->fetchAll (PDO::FETCH_ASSOC);
110		unset ($result);
111		global $triplet_class;
112		foreach ($rows as $row)
113		{
114			$triplet = new $triplet_class ($row['object_id'], $row['vs_id'], $row['rspool_id'], $row);
115			$triplet->display_cells = array ('vs', 'lb', 'rs');
116			$ret[] = $triplet;
117		}
118		return $ret;
119	}
120}
121
122function generateSLBConfig ($triplet_list)
123{
124	$ret = '';
125
126	global $parser_class;
127	$gl_parser = new $parser_class;
128	$defaults = getSLBDefaults (TRUE);
129	$gl_parser->addMacro ('GLOBAL_VS_CONF', dos2unix ($defaults['vsconfig']));
130	$gl_parser->addMacro ('GLOBAL_RS_CONF', dos2unix ($defaults['rsconfig']));
131	$gl_parser->addMacro ('RSPORT', '%VPORT%');
132	$gl_parser->addMacro ('VS_PREPEND',
133"# LB (id == %LB_ID%): %LB_NAME%
134# VS (id == %VS_ID%): %VS_NAME%
135# RS (id == %RSP_ID%): %RSP_NAME%");
136
137	// group triplets by object_id, vs_id
138	$grouped = array();
139	foreach ($triplet_list as $triplet)
140		$grouped[$triplet->lb['id']][$triplet->vs['id']][] = $triplet;
141
142	foreach ($grouped as $object_id => $subarr)
143	{
144		$lb = array_first (array_first ($subarr))->lb;
145		$lb_parser = clone $gl_parser;
146		$lb_parser->addMacro ('LB_ID', $lb['id']);
147		$lb_parser->addMacro ('LB_NAME', $lb['name']);
148		foreach ($subarr as $vs_id => $triplets)
149		{
150			$vs = array_first ($triplets)->vs;
151			$vs_parser = clone $lb_parser;
152			$vs_parser->addMacro ('VS_ID', $vs['id']);
153			$vs_parser->addMacro ('VS_NAME', $vs['name']);
154			$vs_parser->addMacro ('VIP', $vs['vip']);
155			$vs_parser->addMacro ('VPORT', $vs['vport']);
156			$vs_parser->addMacro ('IP_VER', (strlen ($vs['vip_bin']) == 16) ? 6 : 4);
157			if ($vs['proto'] == 'MARK')
158			{
159				$vs_parser->addMacro ('PROTO', 'TCP');
160				$mark = implode ('', unpack ('N', substr ($vs['vip_bin'], 0, 4)));
161				$vs_parser->addMacro ('MARK', $mark);
162				$vs_parser->addMacro ('VS_HEADER', "fwmark $mark");
163			}
164			else
165			{
166				$vs_parser->addMacro ('VS_HEADER', $vs['vip'] . ' ' . $vs['vport']);
167				$vs_parser->addMacro ('PROTO', $vs['proto']);
168			}
169			$vs_parser->addMacro ('VS_RS_CONF', dos2unix ($vs['rsconfig']));
170
171			$vip_bin = ip_checkparse ($vs_parser->expandMacro ('VIP'));
172			if ($vip_bin === FALSE)
173				$family_length = 4;
174			else
175				$family_length = strlen ($vip_bin);
176
177			foreach ($triplets as $triplet)
178			{
179				$rsp = $triplet->rs;
180				$rs_parser = clone $vs_parser;
181				$rs_parser->addMacro ('RSP_ID', $rsp['id']);
182				$rs_parser->addMacro ('RSP_NAME', $rsp['name']);
183				$rs_parser->addMacro ('RSP_VS_CONF', dos2unix ($rsp['vsconfig']));
184				$rs_parser->addMacro ('RSP_RS_CONF', dos2unix ($rsp['rsconfig']));
185				$rs_parser->addMacro ('VS_VS_CONF', dos2unix ($vs['vsconfig'])); // VS-driven vsconfig has higher priority than RSP-driven
186
187				$rs_parser->addMacro ('PRIO', $triplet->slb['prio']);
188				$rs_parser->addMacro ('SLB_VS_CONF', dos2unix ($triplet->slb['vsconfig']));
189				$rs_parser->addMacro ('SLB_RS_CONF', dos2unix ($triplet->slb['rsconfig']));
190
191				$ret .= $rs_parser->expand ("
192%VS_PREPEND%
193virtual_server %VS_HEADER% {
194	protocol %PROTO%
195	%GLOBAL_VS_CONF%
196	%RSP_VS_CONF%
197	%VS_VS_CONF%
198	%SLB_VS_CONF%
199");
200
201				foreach ($rs_parser->getRSList() as $rs_row)
202				{
203					if ($rs_row['inservice'] != 'yes')
204						continue;
205					$parser = clone $rs_parser;
206					$parser->addMacro ('RS_HEADER',  ($parser->expandMacro ('PROTO') == 'MARK' ? '%RSIP%' : '%RSIP% %RSPORT%'));
207					$parser->addMacro ('RSIP', $rs_row['rsip']);
208					if (isset ($rs_row['rsport']))
209						$parser->addMacro ('RSPORT', $rs_row['rsport']);
210					$parser->addMacro ('RS_COMMENT', $rs_row['comment']);
211					$parser->addMacro ('RS_RS_CONF', dos2unix ($rs_row['rsconfig']));
212
213					// do not add v6 reals into v4 service and vice versa
214					$rsip_bin = ip_checkparse ($parser->expandMacro ('RSIP'));
215					if ($rsip_bin !== FALSE && strlen ($rsip_bin) == $family_length)
216						foreach (explode (',', $parser->expandMacro ('RSPORT')) as $rsp_token)
217						{
218							$port_range = explode ('-', $rsp_token);
219							if (count ($port_range) < 1)
220								throw new InvalidArgException ('RSPORT', $rsp_token, "invalid RS port range");
221							if (count ($port_range) < 2)
222								$port_range[] = $port_range[0];
223							if ($port_range[0] > $port_range[1])
224								throw new InvalidArgException ('RSPORT', $rsp_token, "invalid RS port range");
225
226							for ($rsport = $port_range[0]; $rsport <= $port_range[1]; $rsport++)
227							{
228								$r_parser = clone $parser;
229								$r_parser->addMacro ('RSPORT', $rsport);
230								$ret .= $r_parser->expand ("
231	%RS_PREPEND%
232	real_server %RS_HEADER% {
233		%GLOBAL_RS_CONF%
234		%VS_RS_CONF%
235		%RSP_RS_CONF%
236		%SLB_RS_CONF%
237		%RS_RS_CONF%
238	}
239");
240							}
241						}
242				}
243				$ret .= "}\n";
244			}
245		}
246	}
247	return $ret;
248}
249
250class MacroParser
251{
252	protected $macros; // current macro context
253	protected $trace; // recursive macro expansion path
254
255	public function __construct()
256	{
257		$this->macros = array();
258		$this->trace = array();
259	}
260
261	// cuts the subsequent defines from $value and stores the $name-$value define
262	// if $value is unset, returns immediately
263	public function addMacro ($name, $value)
264	{
265		if (! isset ($value))
266			return;
267		$new_value = ''; // value without defines
268		$macro_deep = 0;
269		foreach (explode ("\n", $value) as $line)
270		{
271			if (! $macro_deep)
272			{
273				if (preg_match ('/^([A-Za-z_0-9]+)([\?:]?=)(.*)/', $line, $m))
274				{
275					// found macro definition
276					$mname = $m[1];
277					$op = $m[2];
278					$mvalue = ltrim ($m[3]);
279					if (substr ($mvalue, 0, 1) == '`') // quoted define value
280					{
281						$line = substr ($mvalue, 1);
282						$mvalue = '';
283						$macro_deep++;
284					}
285					else
286					{
287						$mvalue = rtrim ($mvalue);
288						if ($op === ':=')
289							$this->macros[$mname] = $this->expand ($mvalue);
290						elseif ($op === '?=' && '' === $this->expandMacro ($mname))
291							$this->macros[$mname] = $mvalue;
292						else
293							$this->macros[$mname] = $mvalue;
294					}
295				}
296				else
297					$new_value .= $line . "\n";
298			}
299
300			if ($macro_deep)
301			{
302				for ($i = 0; $i < strlen ($line); $i++)
303				{
304					$c = $line[$i];
305					if ($c == "'" && 0 == --$macro_deep)
306					{
307						if ($op === ':=')
308							$this->macros[$mname] = $this->expand ($mvalue);
309						elseif ($op === '?=' && '' === $this->expandMacro ($mname))
310							$this->macros[$mname] = $mvalue;
311						else
312							$this->macros[$mname] = $mvalue;
313						$rest = substr ($line, $i + 1);
314						if (preg_match ('/\S/', $rest))
315							$new_value .= $rest . "\n";
316						break;
317					}
318					elseif ($c == "`")
319						$macro_deep++;
320					$mvalue .= $c;
321				}
322				if ($macro_deep)
323					$mvalue .= "\n";
324			}
325		}
326		$this->macros[$name] = substr ($new_value, 0, -1); // trim last \n
327	}
328
329	// replaces all macro expansions (like %THIS%) by the results of expandMacro calls
330	// Has some formatting logic:
331	//  * indent each line of expansion if the expanding line contains only the macro reference
332	//  * do not add empty line to the output if the expanding line contains only the macro reference and expands to empty string
333	//  * trim last newline of expansion if there already is newline in source string after the macro reference
334	public function expand ($text)
335	{
336		$ret = '';
337		$len = strlen ($text);
338		$pos = 0;
339		$state = 0;
340		$lazy = 0;
341		while ($pos < $len || $pos == $len && $state == 100)
342			switch ($state)
343			{
344				case 99: // expansion failed, cat it untouched into $ret
345					$ret .= '%';
346					$pos = $exp_start + 1;
347					$state = 0;
348					break;
349				case 100: // expand from $exp_start to $pos with $e_value
350					$pre_blank = TRUE;
351					$indent = '';
352					$line_start = 0;
353					for ($i = $exp_start - 1; $i >= 0; $i--)
354						if ($text[$i] == "\n")
355						{
356							$line_start = $i + 1;
357							break;
358						}
359						elseif ($text[$i] != ' ' && $text[$i] != "\t")
360						{
361							$pre_blank = FALSE;
362							break;
363						}
364					if ($pre_blank)
365						$indent = substr ($text, $line_start, $exp_start - $line_start);
366
367					$after_blank = TRUE;
368					$line_end = $len;
369					for ($i = $pos; $i < $len; $i++)
370						if ($text[$i] == "\n")
371						{
372							$line_end = $i;
373							break;
374						}
375						elseif ($text[$i] != ' ' && $text[$i] != "\t")
376						{
377							$after_blank = FALSE;
378							break;
379						}
380					// do actual expansion
381					if ($e_value == '')
382					{
383						// skip entire line if empty expansion was lazy
384						if ($lazy)
385						{
386							$ret = preg_replace ('/.*$/', '', $ret, 1);
387							$pos = $line_end + 1;
388						}
389						// skip newline if expansion is empty and alone
390						elseif ($pre_blank && $after_blank)
391						{
392							$ret = rtrim ($ret, " \t");
393							$pos = $line_end + 1;
394						}
395					}
396					else
397					{
398						// trim last newline of expansion
399						if ($after_blank && $e_value != '' && $e_value[strlen ($e_value) - 1] == "\n")
400							$e_value = substr ($e_value, 0, -1);
401						// indent each line of $e_value
402						if ($indent != '')
403							$e_value = preg_replace ('/\n(?!$)/', "\n$indent", $e_value);
404						$ret .= $e_value;
405					}
406					$lazy = 0;
407					$state = 0;
408					break;
409				case 0: // initial state, search for %
410					if (FALSE === ($exp_start = strpos ($text, '%', $pos)))
411					{
412						$ret .= substr ($text, $pos);
413						$pos = $len;
414					}
415					else
416					{
417						$ret .= substr ($text, $pos, $exp_start - $pos);
418						$pos = $exp_start + 1;
419						$state = 1;
420						if ($pos < $len)
421						{
422							if ($text[$pos] == '{')
423							{
424								$state = 2;
425								$pos++;
426							}
427							elseif ($text[$pos] == '?')
428							{
429								$lazy = 1;
430								$pos++;
431							}
432						}
433						$mname_begin = $pos;
434					}
435					break;
436				case 1: // simple expansion (%ABC%) ending
437					if (preg_match ('/[%\n]/s', $text, $m, PREG_OFFSET_CAPTURE, $pos) && $m[0][0] != "\n")
438					{
439						$i = $m[0][1];
440						$macro_name = substr ($text, $mname_begin, $i - $mname_begin);
441						if (preg_match('/^[A-Za-z_0-9]+$/', $macro_name))
442						{
443							$pos = $i + 1; // skip '%''
444							$e_value = $this->expandMacro ($macro_name);
445							$state = 100;
446							break;
447						}
448					}
449					$state = 99; // rollback expansion
450					break;
451				case 2: // enhanced expansion (%{ABC}% or %{ABC:[+-]smthng}%) stage 1
452					if (preg_match ('/[}:\n]/s', $text, $m, PREG_OFFSET_CAPTURE, $pos) && $m[0][0] != "\n")
453					{
454						$i = $m[0][1];
455						$macro_name = substr ($text, $mname_begin, $i - $mname_begin);
456						if (preg_match('/^[A-Za-z_0-9]+$/', $macro_name))
457						{
458							if ($text[$i] == '}' && $i + 1 < $len && $text[$i + 1] == '%')
459							{
460								$e_value = $this->expandMacro ($macro_name);
461								$pos = $i + 2; // skip '}%'
462								$state = 100;
463								break;
464							}
465							elseif ($text[$i] == ':')
466							{
467								$i++;
468								if ($i < $len && ($text[$i] == '+' || $text[$i] == '-'))
469								{
470									$condition_type = $text[$i];
471									$deep = 1;
472									$pos = $i + 1;
473									$cond_start = $pos;
474									$state = 3;
475									break;
476								}
477							}
478						}
479					}
480					$state = 99; // rollback expansion
481					break;
482				case 3: // conditional expansion (%{ABC:[+-]smthng}%) ending
483					if (! preg_match ('/[{}]/', $text, $m, PREG_OFFSET_CAPTURE, $pos))
484						$state = 99;
485					else
486					{
487						$i = $m[0][1];
488						$pos = $i + 1;
489						if ($text[$i] == '{' && $i > 0 && $text[$i - 1] == '%')
490						{
491							$deep++;
492							$pos++; // skip '%{'
493						}
494						elseif ($text[$i] == '}' && $i + 1 < $len && $text[$i + 1] == '%')
495						{
496							$deep--;
497							$pos++;
498							if ($deep == 0)
499							{
500								$exp = substr ($text, $cond_start, $i - $cond_start);
501								$m_value = $this->expandMacro ($macro_name);
502								if ($condition_type == '+')
503									$e_value = ($m_value == '') ? '' : $this->expand ($exp);
504								elseif ($condition_type == '-')
505									$e_value = ($m_value != '') ? $m_value : $this->expand ($exp);
506								$state = 100;
507							}
508						}
509					}
510					break;
511				default:
512					throw new RackTablesError ("Unexpected state $state in " . __CLASS__ . '::' . __METHOD__ . " FSM", RackTablesError::INTERNAL);
513			}
514
515		if ($state != 0)
516			$ret .= substr ($text, $exp_start);
517		return $ret;
518	}
519
520	// returns the result of expanding the named define, or '' if unset
521	public function expandMacro ($name)
522	{
523		array_push ($this->trace, $name);
524		if (isset ($this->macros[$name]))
525			$ret = $this->expand ($this->macros[$name]);
526		else
527			$ret = '';
528		array_pop ($this->trace);
529		return $ret;
530	}
531
532	// you can inherit the parser class and override this method to fill RS list dynamically
533	public function getRSList()
534	{
535		if (isset ($this->macros['RSP_ID']))
536			return getRSListInPool ($this->macros['RSP_ID']);
537		return array();
538	}
539}
540
541function buildEntityLVSConfig ($cell)
542{
543	$ret = "#\n#\n# This configuration has been generated automatically by RackTables\n#\n#\n";
544	// slbv2
545	if ($cell['realm'] != 'ipv4vs')
546		$ret .= generateSLBConfig2 (getTriplets ($cell));
547	// slbv1
548	if ($cell['realm'] != 'ipvs')
549		$ret .= generateSLBConfig (SLBTriplet::getTriplets ($cell));
550	return $ret;
551}
552
553function buildLVSConfig ($object_id)
554{
555	return callHook ('buildEntityLVSConfig', spotEntity ('object', $object_id));
556}
557
558// *********************  Database functions  *********************
559
560function addRStoRSPool ($pool_id, $rsip_bin, $rsport = 0, $inservice = 'no', $rsconfig = '', $comment = '')
561{
562	$ret = usePreparedInsertBlade
563	(
564		'IPv4RS',
565		array
566		(
567			'rspool_id' => $pool_id,
568			'rsip' => $rsip_bin,
569			'rsport' => ($rsport == '' || $rsport === 0) ? NULL : $rsport,
570			'inservice' => $inservice == 'yes' ? 'yes' : 'no',
571			'rsconfig' => nullIfEmptyStr ($rsconfig),
572			'comment' => nullIfEmptyStr ($comment),
573		)
574	);
575	lastCreated ('iprs', lastInsertID());
576	return $ret;
577}
578
579function addLBtoRSPool ($pool_id = 0, $object_id = 0, $vs_id = 0, $vsconfig = '', $rsconfig = '', $prio = '')
580{
581	usePreparedInsertBlade
582	(
583		'IPv4LB',
584		array
585		(
586			'object_id' => $object_id,
587			'rspool_id' => $pool_id,
588			'vs_id' => $vs_id,
589			'vsconfig' => nullIfEmptyStr ($vsconfig),
590			'rsconfig' => nullIfEmptyStr ($rsconfig),
591			'prio' => nullIfEmptyStr ($prio),
592		)
593	);
594}
595
596function commitDeleteVS ($id = 0)
597{
598	releaseFiles ('ipv4vs', $id);
599	destroyTagsForEntity ('ipv4vs', $id);
600	usePreparedDeleteBlade ('IPv4VS', array ('id' => $id));
601}
602
603function commitUpdateRS ($rsid, $rsip_bin, $rsport = 0, $inservice = 'yes', $rsconfig = '', $comment = '')
604{
605	usePreparedExecuteBlade
606	(
607		'UPDATE IPv4RS SET rsip=?, rsport=?, inservice=?, rsconfig=?, comment=? WHERE id=?',
608		array
609		(
610			$rsip_bin,
611			($rsport == '' || $rsport === 0) ? NULL : $rsport,
612			$inservice,
613			nullIfEmptyStr ($rsconfig),
614			nullIfEmptyStr ($comment),
615			$rsid,
616		)
617	);
618}
619
620// $vport is ignored if $proto == 'MARK'
621function commitUpdateVS ($vsid, $vip_bin, $vport = 0, $proto = '', $name = '', $vsconfig = '', $rsconfig = '')
622{
623	if ($proto != 'MARK' && $vport <= 0)
624		throw new InvalidArgException ('vport', $vport);
625	if ($proto == '')
626		throw new InvalidArgException ('proto', $proto);
627	return usePreparedUpdateBlade
628	(
629		'IPv4VS',
630		array
631		(
632			'vip' => $vip_bin,
633			'vport' => ($proto == 'MARK' ? NULL : $vport),
634			'proto' => $proto,
635			'name' => nullIfEmptyStr ($name),
636			'vsconfig' => nullIfEmptyStr ($vsconfig),
637			'rsconfig' => nullIfEmptyStr ($rsconfig),
638		),
639		array ('id' => $vsid)
640	);
641}
642
643function commitCreateRSPool ($name = '', $vsconfig = '', $rsconfig = '', $tagidlist = array())
644{
645	usePreparedInsertBlade
646	(
647		'IPv4RSPool',
648		array
649		(
650			'name' => nullIfEmptyStr ($name),
651			'vsconfig' => nullIfEmptyStr ($vsconfig),
652			'rsconfig' => nullIfEmptyStr ($rsconfig),
653		)
654	);
655	$new_pool_id = lastInsertID();
656	lastCreated ('ipv4rspool', $new_pool_id);
657	produceTagsForNewRecord ('ipv4rspool', $tagidlist, $new_pool_id);
658	return $new_pool_id;
659}
660
661function commitDeleteRSPool ($pool_id = 0)
662{
663	releaseFiles ('ipv4rspool', $pool_id);
664	destroyTagsForEntity ('ipv4rspool', $pool_id);
665	usePreparedDeleteBlade ('IPv4RSPool', array ('id' => $pool_id));
666}
667
668function commitUpdateSLBDefConf ($data)
669{
670	saveScript('DefaultVSConfig', $data['vs']);
671	saveScript('DefaultRSConfig', $data['rs']);
672}
673
674function getSLBDefaults ($do_cache_result = FALSE)
675{
676	static $ret = array();
677
678	if (! $do_cache_result)
679		$ret = array();
680	elseif (count ($ret))
681		return $ret;
682
683	$ret['vsconfig'] = loadScript ('DefaultVSConfig');
684	$ret['rsconfig'] = loadScript ('DefaultRSConfig');
685	return $ret;
686}
687
688// Return the list of all currently configured load balancers with their pool count.
689function getLBList ()
690{
691	$result = usePreparedSelectBlade
692	(
693		"select object_id, count(rspool_id) as poolcount " .
694		"from IPv4LB group by object_id order by object_id"
695	);
696	$ret = array ();
697	while ($row = $result->fetch (PDO::FETCH_ASSOC))
698		$ret[$row['object_id']] = $row['poolcount'];
699	return $ret;
700}
701
702function getRSList ()
703{
704	$result = usePreparedSelectBlade
705	(
706		"select id, inservice, rsip as rsip_bin, rsport, rspool_id, rsconfig " .
707		"from IPv4RS order by rspool_id, IPv4RS.rsip, rsport"
708	);
709	$ret = array ();
710	while ($row = $result->fetch (PDO::FETCH_ASSOC))
711	{
712		$row['rsip'] = ip_format ($row['rsip_bin']);
713		$row['rsconfig'] = dos2unix ($row['rsconfig']);
714		foreach (array ('inservice', 'rsip_bin', 'rsip', 'rsport', 'rspool_id', 'rsconfig') as $cname)
715			$ret[$row['id']][$cname] = $row[$cname];
716	}
717	return $ret;
718}
719
720function getRSListInPool ($rspool_id)
721{
722	$ret = array();
723	$query = "select id, inservice, rsip as rsip_bin, rsport, rsconfig, comment from " .
724		"IPv4RS where rspool_id = ? order by IPv4RS.rsip, rsport";
725	$result = usePreparedSelectBlade ($query, array ($rspool_id));
726	while ($row = $result->fetch (PDO::FETCH_ASSOC))
727	{
728		$row['rsip'] = ip_format ($row['rsip_bin']);
729		$ret[$row['id']] = $row;
730	}
731	return $ret;
732}
733