allow to delete local user accounts (Mantis#1089)
[racktables] / wwwroot / inc / ophandlers.php
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 /*
8
9 "Ophandler" in RackTables stands for "operation handler", or a function
10 that handles execution of "operation" (in the meaning explained in
11 navigation.php). Most of the ophandlers are meant to perform one specific
12 action, for example, to set a name of an object. Each such action often
13 requires a set of parameters (e. g. ID of the object and the new name),
14 and it is responsibility of each ophandler function to verify that all
15 necessary parameters are provided by the user and have proper values. There
16 is a number of helper functions to make such verification simpler.
17
18 Errors occuring in ophandlers are typically indicated with exceptions of
19 assorted classes. Namely, an "InvalidRequestArgException" class means that
20 at least one of the parameters provided by the user is not acceptable. This
21 is a "soft" error, which gets displayed in the standard message area of
22 otherwise usual interface. A different case is "InvalidArgException", which
23 means that one of the internal functions detected its argument(s) invalid
24 or corrupted, and that argument(s) did not come from user's input (and thus
25 cannot be fixed without fixing a bug in the code). Such "hard" errors don't
26 get special early handling and end up in the default catching block. The
27 latter may print a detailed stack trace instead of the interface HTML to
28 help a developer debug the issue.
29
30 As long as an ophandler makes through its request (extracting arguments,
31 performing validation and actually updating records in the database), it
32 may queue up messages (often referred to as "green" and "red" bars) by
33 means of showError() and showSuccess() functions. The messages are not
34 displayed immediately, because successfull ophandlers are expected to
35 return only the new URL, where the user will be immediately redirected to
36 (it is also possible to return an empty string to mean that the current
37 logical location remains the same). The page at the "next" location is
38 supposed to translate message buffer into the standard message area.
39
40 A very special case of an ophandler is tableHandler(). This generic
41 function handles the most trivial actions, which map to a single INSERT,
42 UPDATE or DELETE SQL statement with a fixed number of arguments. The rules
43 of argument validation and mapping are listed in $opspec_list (operation
44 specifications list) array.
45
46 */
47
48 // This array is deprecated. Please do not add new message constants to it.
49 // use the new showError, showWarning, showSuccess functions instead
50 global $msgcode;
51 $msgcode = array();
52
53 global $opspec_list;
54 $opspec_list = array();
55
56 $opspec_list['object-edit-unlinkObjects'] = array
57 (
58 'table' => 'EntityLink',
59 'action' => 'DELETE',
60 'arglist' => array
61 (
62 array ('url_argname' => 'link_id', 'table_colname' => 'id', 'assertion' => 'natural'),
63 ),
64 );
65 $opspec_list['object-ports-useup'] = array
66 (
67 'table' => 'Port',
68 'action' => 'UPDATE',
69 'set_arglist' => array
70 (
71 array ('fix_argname' => 'reservation_comment', 'fix_argvalue' => NULL),
72 ),
73 'where_arglist' => array
74 (
75 array ('url_argname' => 'port_id', 'table_colname' => 'id', 'assertion' => 'natural'),
76 array ('url_argname' => 'object_id', 'assertion' => 'natural'), # preserve context
77 ),
78 );
79 $opspec_list['object-ports-delPort'] = array
80 (
81 'table' => 'Port',
82 'action' => 'DELETE',
83 'arglist' => array
84 (
85 array ('url_argname' => 'port_id', 'table_colname' => 'id', 'assertion' => 'natural'),
86 array ('url_argname' => 'object_id', 'assertion' => 'natural'),
87 ),
88 );
89 $opspec_list['object-ports-deleteAll'] = array
90 (
91 'table' => 'Port',
92 'action' => 'DELETE',
93 'arglist' => array
94 (
95 array ('url_argname' => 'object_id', 'assertion' => 'natural'),
96 ),
97 );
98 $opspec_list['location-log-del'] = array
99 (
100 'table' => 'ObjectLog',
101 'action' => 'DELETE',
102 'arglist' => array
103 (
104 array ('url_argname' => 'log_id', 'table_colname' => 'id', 'assertion' => 'natural'),
105 array ('url_argname' => 'location_id', 'table_colname' => 'object_id', 'assertion' => 'natural'),
106 ),
107 );
108 $opspec_list['object-log-del'] = array
109 (
110 'table' => 'ObjectLog',
111 'action' => 'DELETE',
112 'arglist' => array
113 (
114 array ('url_argname' => 'log_id', 'table_colname' => 'id', 'assertion' => 'natural'),
115 array ('url_argname' => 'object_id', 'assertion' => 'natural'),
116 ),
117 );
118 $opspec_list['rack-log-del'] = array
119 (
120 'table' => 'ObjectLog',
121 'action' => 'DELETE',
122 'arglist' => array
123 (
124 array ('url_argname' => 'log_id', 'table_colname' => 'id', 'assertion' => 'natural'),
125 array ('url_argname' => 'rack_id', 'table_colname' => 'object_id', 'assertion' => 'natural'),
126 ),
127 );
128 $opspec_list['row-log-del'] = array
129 (
130 'table' => 'ObjectLog',
131 'action' => 'DELETE',
132 'arglist' => array
133 (
134 array ('url_argname' => 'log_id', 'table_colname' => 'id', 'assertion' => 'natural'),
135 array ('url_argname' => 'row_id', 'table_colname' => 'object_id', 'assertion' => 'natural'),
136 ),
137 );
138 $opspec_list['ipv4vs-editlblist-delLB'] =
139 $opspec_list['ipv4rspool-editlblist-delLB'] =
140 $opspec_list['object-editrspvs-delLB'] = array
141 (
142 'table' => 'IPv4LB',
143 'action' => 'DELETE',
144 'arglist' => array
145 (
146 array ('url_argname' => 'object_id', 'assertion' => 'natural'),
147 array ('url_argname' => 'pool_id', 'table_colname' => 'rspool_id', 'assertion' => 'natural'),
148 array ('url_argname' => 'vs_id', 'assertion' => 'natural'),
149 ),
150 );
151 $opspec_list['ipv4vs-editlblist-updLB'] =
152 $opspec_list['ipv4rspool-editlblist-updLB'] =
153 $opspec_list['object-editrspvs-updLB'] = array
154 (
155 'table' => 'IPv4LB',
156 'action' => 'UPDATE',
157 'set_arglist' => array
158 (
159 array ('url_argname' => 'vsconfig', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
160 array ('url_argname' => 'rsconfig', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
161 array ('url_argname' => 'prio', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
162 ),
163 'where_arglist' => array
164 (
165 array ('url_argname' => 'object_id', 'assertion' => 'natural'),
166 array ('url_argname' => 'pool_id', 'table_colname' => 'rspool_id', 'assertion' => 'natural'),
167 array ('url_argname' => 'vs_id', 'assertion' => 'natural'),
168 ),
169 );
170 $opspec_list['ipv4rspool-editrslist-delRS'] = array
171 (
172 'table' => 'IPv4RS',
173 'action' => 'DELETE',
174 'arglist' => array
175 (
176 array ('url_argname' => 'id', 'assertion' => 'natural'),
177 ),
178 );
179 $opspec_list['parentmap-edit-add'] = array
180 (
181 'table' => 'ObjectParentCompat',
182 'action' => 'INSERT',
183 'arglist' => array
184 (
185 array ('url_argname' => 'parent_objtype_id', 'assertion' => 'natural'),
186 array ('url_argname' => 'child_objtype_id', 'assertion' => 'natural'),
187 ),
188 );
189 $opspec_list['parentmap-edit-del'] = array
190 (
191 'table' => 'ObjectParentCompat',
192 'action' => 'DELETE',
193 'arglist' => array
194 (
195 array ('url_argname' => 'parent_objtype_id', 'assertion' => 'natural'),
196 array ('url_argname' => 'child_objtype_id', 'assertion' => 'natural'),
197 ),
198 );
199 $opspec_list['portifcompat-edit-add'] = array
200 (
201 'table' => 'PortInterfaceCompat',
202 'action' => 'INSERT',
203 'arglist' => array
204 (
205 array ('url_argname' => 'iif_id', 'assertion' => 'natural'),
206 array ('url_argname' => 'oif_id', 'assertion' => 'natural'),
207 ),
208 );
209 $opspec_list['portifcompat-edit-del'] = array
210 (
211 'table' => 'PortInterfaceCompat',
212 'action' => 'DELETE',
213 'arglist' => array
214 (
215 array ('url_argname' => 'iif_id', 'assertion' => 'natural'),
216 array ('url_argname' => 'oif_id', 'assertion' => 'natural'),
217 ),
218 );
219 $opspec_list['portoifs-edit-add'] = array
220 (
221 'table' => 'PortOuterInterface',
222 'action' => 'INSERT',
223 'arglist' => array
224 (
225 array ('url_argname' => 'oif_name', 'assertion' => 'string'),
226 ),
227 );
228 $opspec_list['portoifs-edit-del'] = array
229 (
230 'table' => 'PortOuterInterface',
231 'action' => 'DELETE',
232 'arglist' => array
233 (
234 array ('url_argname' => 'id', 'assertion' => 'natural'),
235 ),
236 );
237 $opspec_list['portoifs-edit-upd'] = array
238 (
239 'table' => 'PortOuterInterface',
240 'action' => 'UPDATE',
241 'set_arglist' => array
242 (
243 array ('url_argname' => 'oif_name', 'assertion' => 'string'),
244 ),
245 'where_arglist' => array
246 (
247 array ('url_argname' => 'id', 'assertion' => 'natural'),
248 ),
249 );
250 $opspec_list['attrs-editmap-del'] = array
251 (
252 'table' => 'AttributeMap',
253 'action' => 'DELETE',
254 'arglist' => array
255 (
256 array ('url_argname' => 'attr_id', 'assertion' => 'natural'),
257 array ('url_argname' => 'objtype_id', 'assertion' => 'natural'),
258 ),
259 );
260 $opspec_list['attrs-editattrs-add'] = array
261 (
262 'table' => 'Attribute',
263 'action' => 'INSERT',
264 'arglist' => array
265 (
266 array ('url_argname' => 'attr_type', 'table_colname' => 'type', 'assertion' => 'enum/attr_type'),
267 array ('url_argname' => 'attr_name', 'table_colname' => 'name', 'assertion' => 'string'),
268 ),
269 );
270 $opspec_list['attrs-editattrs-del'] = array
271 (
272 'table' => 'Attribute',
273 'action' => 'DELETE',
274 'arglist' => array
275 (
276 array ('url_argname' => 'attr_id', 'table_colname' => 'id', 'assertion' => 'natural'),
277 ),
278 );
279 $opspec_list['attrs-editattrs-upd'] = array
280 (
281 'table' => 'Attribute',
282 'action' => 'UPDATE',
283 'set_arglist' => array
284 (
285 array ('url_argname' => 'attr_name', 'table_colname' => 'name', 'assertion' => 'string'),
286 ),
287 'where_arglist' => array
288 (
289 array ('url_argname' => 'attr_id', 'table_colname' => 'id', 'assertion' => 'natural'),
290 ),
291 );
292 $opspec_list['dict-chapters-add'] = array
293 (
294 'table' => 'Chapter',
295 'action' => 'INSERT',
296 'arglist' => array
297 (
298 array ('url_argname' => 'chapter_name', 'table_colname' => 'name', 'assertion' => 'string')
299 ),
300 );
301 $opspec_list['chapter-edit-add'] = array
302 (
303 'table' => 'Dictionary',
304 'action' => 'INSERT',
305 'arglist' => array
306 (
307 array ('url_argname' => 'chapter_no', 'table_colname' => 'chapter_id', 'assertion' => 'natural'),
308 array ('url_argname' => 'dict_value', 'assertion' => 'string'),
309 ),
310 );
311 $opspec_list['chapter-edit-del'] = array
312 (
313 'table' => 'Dictionary',
314 'action' => 'DELETE',
315 'arglist' => array
316 (
317 // Technically dict_key is enough to delete, but including chapter_id into
318 // WHERE clause makes sure that the action actually happends for the same
319 // chapter that authorization was granted for.
320 array ('url_argname' => 'chapter_no', 'table_colname' => 'chapter_id', 'assertion' => 'natural'),
321 array ('url_argname' => 'dict_key', 'assertion' => 'natural'),
322 array ('fix_argname' => 'dict_sticky', 'fix_argvalue' => 'no'), # protect system rows
323 ),
324 );
325 $opspec_list['chapter-edit-upd'] = array
326 (
327 'table' => 'Dictionary',
328 'action' => 'UPDATE',
329 'set_arglist' => array
330 (
331 array ('url_argname' => 'dict_value', 'assertion' => 'string'),
332 ),
333 'where_arglist' => array
334 (
335 # same as above for listing chapter_no
336 array ('url_argname' => 'chapter_no', 'table_colname' => 'chapter_id', 'assertion' => 'natural'),
337 array ('url_argname' => 'dict_key', 'assertion' => 'natural'),
338 array ('fix_argname' => 'dict_sticky', 'fix_argvalue' => 'no'), # protect system rows
339 ),
340 );
341 $opspec_list['tagtree-edit-createTag'] = array
342 (
343 'table' => 'TagTree',
344 'action' => 'INSERT',
345 'arglist' => array
346 (
347 array ('url_argname' => 'tag_name', 'table_colname' => 'tag', 'assertion' => 'tag'),
348 array ('url_argname' => 'parent_id', 'assertion' => 'unsigned', 'translator' => 'nullIfZero'),
349 array ('url_argname' => 'is_assignable', 'assertion' => 'enum/yesno'),
350 array ('url_argname' => 'color', 'assertion' => 'htmlcolor0', 'translator' => 'HTMLColorForDatabase'),
351 ),
352 );
353 $opspec_list['tagtree-edit-destroyTag'] = array
354 (
355 'table' => 'TagTree',
356 'action' => 'DELETE',
357 'arglist' => array
358 (
359 array ('url_argname' => 'tag_id', 'table_colname' => 'id', 'assertion' => 'natural'),
360 ),
361 );
362 $opspec_list['tagtree-descriptions-updTagDescr'] = array
363 (
364 'table' => 'TagTree',
365 'action' => 'UPDATE',
366 'set_arglist' => array
367 (
368 array ('url_argname' => 'description', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
369 ),
370 'where_arglist' => array
371 (
372 array ('url_argname' => 'id', 'assertion' => 'natural'),
373 ),
374 );
375 $opspec_list['8021q-vstlist-add'] = array
376 (
377 'table' => 'VLANSwitchTemplate',
378 'action' => 'INSERT',
379 'arglist' => array
380 (
381 array ('url_argname' => 'vst_descr', 'table_colname' => 'description', 'assertion' => 'string'),
382 // workaround SQL_STRICT
383 array ('fix_argname' => 'mutex_rev', 'fix_argvalue' => 0),
384 array ('fix_argname' => 'saved_by', 'fix_argvalue' => ""),
385 ),
386 );
387 $opspec_list['8021q-vstlist-del'] = array
388 (
389 'table' => 'VLANSwitchTemplate',
390 'action' => 'DELETE',
391 'arglist' => array
392 (
393 array ('url_argname' => 'vst_id', 'table_colname' => 'id', 'assertion' => 'natural'),
394 ),
395 );
396 $opspec_list['8021q-vstlist-upd'] = array
397 (
398 'table' => 'VLANSwitchTemplate',
399 'action' => 'UPDATE',
400 'set_arglist' => array
401 (
402 array ('url_argname' => 'vst_descr', 'table_colname' => 'description', 'assertion' => 'string'),
403 ),
404 'where_arglist' => array
405 (
406 array ('url_argname' => 'vst_id', 'table_colname' => 'id', 'assertion' => 'natural'),
407 ),
408 );
409 $opspec_list['8021q-vdlist-del'] = array
410 (
411 'table' => 'VLANDomain',
412 'action' => 'DELETE',
413 'arglist' => array
414 (
415 array ('url_argname' => 'vdom_id', 'table_colname' => 'id', 'assertion' => 'natural'),
416 ),
417 );
418 $opspec_list['vlandomain-vlanlist-add'] = array
419 (
420 'table' => 'VLANDescription',
421 'action' => 'INSERT',
422 'arglist' => array
423 (
424 array ('url_argname' => 'vdom_id', 'table_colname' => 'domain_id', 'assertion' => 'natural'),
425 array ('url_argname' => 'vlan_id', 'assertion' => 'vlan'),
426 array ('url_argname' => 'vlan_type', 'assertion' => 'enum/vlan_type'),
427 array ('url_argname' => 'vlan_descr', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
428 ),
429 );
430 $opspec_list['vlandomain-vlanlist-del'] = array
431 (
432 'table' => 'VLANDescription',
433 'action' => 'DELETE',
434 'arglist' => array
435 (
436 array ('url_argname' => 'vdom_id', 'table_colname' => 'domain_id', 'assertion' => 'natural'),
437 array ('url_argname' => 'vlan_id', 'assertion' => 'vlan'),
438 ),
439 );
440 $opspec_list['vlan-edit-upd'] = // both locations are using the same tableHandler op
441 $opspec_list['vlandomain-vlanlist-upd'] = array
442 (
443 'table' => 'VLANDescription',
444 'action' => 'UPDATE',
445 'set_arglist' => array
446 (
447 array ('url_argname' => 'vlan_type', 'assertion' => 'enum/vlan_type'),
448 array ('url_argname' => 'vlan_descr', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
449 ),
450 'where_arglist' => array
451 (
452 array ('url_argname' => 'vdom_id', 'table_colname' => 'domain_id', 'assertion' => 'natural'),
453 array ('url_argname' => 'vlan_id', 'assertion' => 'vlan'),
454 ),
455 );
456 $opspec_list['dict-chapters-upd'] = array
457 (
458 'table' => 'Chapter',
459 'action' => 'UPDATE',
460 'set_arglist' => array
461 (
462 array ('url_argname' => 'chapter_name', 'table_colname' => 'name', 'assertion' => 'string'),
463 ),
464 'where_arglist' => array
465 (
466 array ('url_argname' => 'chapter_no', 'table_colname' => 'id', 'assertion' => 'natural'),
467 array ('fix_argname' => 'sticky', 'fix_argvalue' => 'no'), # protect system chapters
468 ),
469 );
470 $opspec_list['dict-chapters-del'] = array
471 (
472 'table' => 'Chapter',
473 'action' => 'DELETE',
474 'arglist' => array
475 (
476 array ('url_argname' => 'chapter_no', 'table_colname' => 'id', 'assertion' => 'natural'),
477 array ('fix_argname' => 'sticky', 'fix_argvalue' => 'no'), # protect system chapters
478 ),
479 );
480 $opspec_list['cables-heaps-add'] = array
481 (
482 'table' => 'PatchCableHeap',
483 'action' => 'INSERT',
484 'arglist' => array
485 (
486 array ('url_argname' => 'end1_conn_id', 'assertion' => 'natural'),
487 array ('url_argname' => 'pctype_id', 'assertion' => 'natural'),
488 array ('url_argname' => 'end2_conn_id', 'assertion' => 'natural'),
489 array ('fix_argname' => 'amount', 'fix_argvalue' => 0),
490 array ('url_argname' => 'length', 'assertion' => 'decimal'),
491 array ('url_argname' => 'description', 'assertion' => 'string0'),
492 ),
493 );
494 $opspec_list['cables-heaps-del'] = array
495 (
496 'table' => 'PatchCableHeap',
497 'action' => 'DELETE',
498 'arglist' => array
499 (
500 array ('url_argname' => 'id', 'assertion' => 'natural'),
501 ),
502 );
503 $opspec_list['cables-heaps-upd'] = array
504 (
505 'table' => 'PatchCableHeap',
506 'action' => 'UPDATE',
507 'set_arglist' => array
508 (
509 array ('url_argname' => 'end1_conn_id', 'assertion' => 'natural'),
510 array ('url_argname' => 'pctype_id', 'assertion' => 'natural'),
511 array ('url_argname' => 'end2_conn_id', 'assertion' => 'natural'),
512 array ('url_argname' => 'length', 'assertion' => 'decimal'),
513 array ('url_argname' => 'description', 'assertion' => 'string0'),
514 ),
515 'where_arglist' => array
516 (
517 array ('url_argname' => 'id', 'assertion' => 'natural'),
518 ),
519 );
520 $opspec_list['cableconf-connectors-add'] = array
521 (
522 'table' => 'PatchCableConnector',
523 'action' => 'INSERT',
524 'arglist' => array
525 (
526 array ('url_argname' => 'connector', 'assertion' => 'string'),
527 array ('fix_argname' => 'origin', 'fix_argvalue' => 'custom'),
528 ),
529 );
530 $opspec_list['cableconf-connectors-del'] = array
531 (
532 'table' => 'PatchCableConnector',
533 'action' => 'DELETE',
534 'arglist' => array
535 (
536 array ('url_argname' => 'id', 'assertion' => 'natural'),
537 array ('fix_argname' => 'origin', 'fix_argvalue' => 'custom'),
538 ),
539 );
540 $opspec_list['cableconf-connectors-upd'] = array
541 (
542 'table' => 'PatchCableConnector',
543 'action' => 'UPDATE',
544 'set_arglist' => array
545 (
546 array ('url_argname' => 'connector', 'assertion' => 'string'),
547 ),
548 'where_arglist' => array
549 (
550 array ('url_argname' => 'id', 'assertion' => 'natural'),
551 array ('fix_argname' => 'origin', 'fix_argvalue' => 'custom'),
552 ),
553 );
554 $opspec_list['cableconf-cabletypes-add'] = array
555 (
556 'table' => 'PatchCableType',
557 'action' => 'INSERT',
558 'arglist' => array
559 (
560 array ('url_argname' => 'pctype', 'assertion' => 'string'),
561 array ('fix_argname' => 'origin', 'fix_argvalue' => 'custom'),
562 ),
563 );
564 $opspec_list['cableconf-cabletypes-del'] = array
565 (
566 'table' => 'PatchCableType',
567 'action' => 'DELETE',
568 'arglist' => array
569 (
570 array ('url_argname' => 'id', 'assertion' => 'natural'),
571 array ('fix_argname' => 'origin', 'fix_argvalue' => 'custom'),
572 ),
573 );
574 $opspec_list['cableconf-cabletypes-upd'] = array
575 (
576 'table' => 'PatchCableType',
577 'action' => 'UPDATE',
578 'set_arglist' => array
579 (
580 array ('url_argname' => 'pctype', 'assertion' => 'string'),
581 ),
582 'where_arglist' => array
583 (
584 array ('url_argname' => 'id', 'assertion' => 'natural'),
585 array ('fix_argname' => 'origin', 'fix_argvalue' => 'custom'),
586 ),
587 );
588 $opspec_list['cableconf-conncompat-add'] = array
589 (
590 'table' => 'PatchCableConnectorCompat',
591 'action' => 'INSERT',
592 'arglist' => array
593 (
594 array ('url_argname' => 'pctype_id', 'assertion' => 'natural'),
595 array ('url_argname' => 'connector_id', 'assertion' => 'natural'),
596 ),
597 );
598 $opspec_list['cableconf-conncompat-del'] = array
599 (
600 'table' => 'PatchCableConnectorCompat',
601 'action' => 'DELETE',
602 'arglist' => array
603 (
604 array ('url_argname' => 'pctype_id', 'assertion' => 'natural'),
605 array ('url_argname' => 'connector_id', 'assertion' => 'natural'),
606 ),
607 );
608 $opspec_list['cableconf-oifcompat-add'] = array
609 (
610 'table' => 'PatchCableOIFCompat',
611 'action' => 'INSERT',
612 'arglist' => array
613 (
614 array ('url_argname' => 'pctype_id', 'assertion' => 'natural'),
615 array ('url_argname' => 'oif_id', 'assertion' => 'natural'),
616 ),
617 );
618 $opspec_list['cableconf-oifcompat-del'] = array
619 (
620 'table' => 'PatchCableOIFCompat',
621 'action' => 'DELETE',
622 'arglist' => array
623 (
624 array ('url_argname' => 'pctype_id', 'assertion' => 'natural'),
625 array ('url_argname' => 'oif_id', 'assertion' => 'natural'),
626 ),
627 );
628 $opspec_list['plugins-edit-disable'] = array
629 (
630 'table' => 'Plugin',
631 'action' => 'UPDATE',
632 'set_arglist' => array
633 (
634 array ('fix_argname' => 'state', 'fix_argvalue' => 'disabled'),
635 ),
636 'where_arglist' => array
637 (
638 array ('url_argname' => 'name', 'assertion' => 'string'),
639 ),
640 );
641 $opspec_list['plugins-edit-enable'] = array
642 (
643 'table' => 'Plugin',
644 'action' => 'UPDATE',
645 'set_arglist' => array
646 (
647 array ('fix_argname' => 'state', 'fix_argvalue' => 'enabled'),
648 ),
649 'where_arglist' => array
650 (
651 array ('url_argname' => 'name', 'assertion' => 'string'),
652 ),
653 );
654
655 function setFuncMessages ($funcname, $messages)
656 {
657 global $msgcode;
658 foreach ($messages as $symbol => $code)
659 $msgcode[$funcname][$symbol] = $code;
660 }
661
662 function addPortForwarding ()
663 {
664 setFuncMessages (__FUNCTION__, array ('OK' => 48));
665 $proto = genericAssertion ('proto', 'enum/natv4proto');
666 if ($proto != 'ALL')
667 {
668 genericAssertion ('localport', 'natural');
669 genericAssertion ('remoteport', 'natural');
670 }
671 assertStringArg ('description', TRUE);
672 $remoteport = isset ($_REQUEST['remoteport']) ? $_REQUEST['remoteport'] : '';
673 if ($remoteport == '')
674 $remoteport = $_REQUEST['localport'];
675
676 try
677 {
678 newPortForwarding
679 (
680 getBypassValue(),
681 genericAssertion ('localip', 'inet4'),
682 $_REQUEST['localport'],
683 genericAssertion ('remoteip', 'inet4'),
684 $remoteport,
685 $proto,
686 $_REQUEST['description']
687 );
688 }
689 catch (InvalidArgException $iae)
690 {
691 throw $iae->newIRAE();
692 }
693 showFuncMessage (__FUNCTION__, 'OK');
694 }
695
696 function delPortForwarding ()
697 {
698 setFuncMessages (__FUNCTION__, array ('OK' => 49));
699 $proto = genericAssertion ('proto', 'enum/natv4proto');
700 if ($proto != 'ALL')
701 {
702 genericAssertion ('localport', 'natural');
703 genericAssertion ('remoteport', 'natural');
704 }
705
706 deletePortForwarding
707 (
708 getBypassValue(),
709 genericAssertion ('localip', 'inet4'),
710 $_REQUEST['localport'],
711 genericAssertion ('remoteip', 'inet4'),
712 $_REQUEST['remoteport'],
713 $proto
714 );
715 showFuncMessage (__FUNCTION__, 'OK');
716 }
717
718 function updPortForwarding ()
719 {
720 setFuncMessages (__FUNCTION__, array ('OK' => 51));
721 $proto = genericAssertion ('proto', 'enum/natv4proto');
722 if ($proto != 'ALL')
723 {
724 genericAssertion ('localport', 'natural');
725 genericAssertion ('remoteport', 'natural');
726 }
727 assertStringArg ('description', TRUE);
728
729 updatePortForwarding
730 (
731 getBypassValue(),
732 genericAssertion ('localip', 'inet4'),
733 $_REQUEST['localport'],
734 genericAssertion ('remoteip', 'inet4'),
735 $_REQUEST['remoteport'],
736 $proto,
737 $_REQUEST['description']
738 );
739 showFuncMessage (__FUNCTION__, 'OK');
740 }
741
742 function addPortForObject ()
743 {
744 setFuncMessages (__FUNCTION__, array ('OK' => 48));
745 genericAssertion ('port_name', 'string');
746 try
747 {
748 commitAddPort
749 (
750 getBypassValue(),
751 trim ($_REQUEST['port_name']),
752 genericAssertion ('port_type_id', 'string'),
753 trim ($_REQUEST['port_label']),
754 trim (genericAssertion ('port_l2address', 'l2address0'))
755 );
756 }
757 catch (InvalidRequestArgException $irae)
758 {
759 throw $irae;
760 }
761 catch (InvalidArgException $iae)
762 {
763 throw $iae->newIRAE();
764 }
765 showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['port_name']));
766 }
767
768 function editPortForObject ()
769 {
770 setFuncMessages (__FUNCTION__, array ('OK' => 6));
771 global $sic;
772 $port_id = genericAssertion ('port_id', 'natural');
773 try
774 {
775 commitUpdatePort
776 (
777 getBypassValue(),
778 $port_id,
779 genericAssertion ('name', 'string'),
780 assertStringArg ('port_type_id'),
781 genericAssertion ('label', 'string0'),
782 genericAssertion ('l2address', 'l2address0'),
783 assertStringArg ('reservation_comment', TRUE)
784 );
785 }
786 catch (InvalidRequestArgException $irae)
787 {
788 throw $irae;
789 }
790 catch (InvalidArgException $iae)
791 {
792 throw $iae->newIRAE();
793 }
794 if (array_key_exists ('cable', $_REQUEST))
795 commitUpdatePortLink ($port_id, $sic['cable']);
796 showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
797 }
798
799 function addMultiPorts ()
800 {
801 setFuncMessages (__FUNCTION__, array ('OK' => 10));
802 assertStringArg ('input');
803 $format = genericAssertion ('format', 'string');
804 $port_type = genericAssertion ('port_type', 'string');
805 $object_id = getBypassValue();
806 $ports = array();
807 foreach (textareaCooked ($_REQUEST['input']) as $line)
808 {
809 switch ($format)
810 {
811 case 'ssv1':
812 $words = explode (' ', $line);
813 if ($words[0] == '') // empty L2 address is OK
814 continue;
815 $ports[] = array
816 (
817 'name' => $words[0],
818 'l2address' => array_fetch ($words, 1, ''),
819 'label' => ''
820 );
821 break;
822 default:
823 throw new RackTablesError ("unknown data format '${format}'", RackTablesError::INTERNAL);
824 }
825 }
826 // Create ports, if they don't exist.
827 $added_count = $updated_count = $error_count = 0;
828 foreach ($ports as $port)
829 {
830 $port_ids = getPortIDs ($object_id, $port['name']);
831 try
832 {
833 if (!count ($port_ids))
834 {
835 commitAddPort ($object_id, $port['name'], $port_type, $port['label'], $port['l2address']);
836 $added_count++;
837 }
838 elseif (count ($port_ids) == 1) // update only single-socket ports
839 {
840 $rsvc = getPortReservationComment (array_first ($port_ids));
841 commitUpdatePort ($object_id, $port_ids[0], $port['name'], $port_type, $port['label'], $port['l2address'], $rsvc);
842 $updated_count++;
843 }
844 }
845 catch (InvalidArgException $iae)
846 {
847 showError ($iae->getMessage());
848 }
849 }
850 showFuncMessage (__FUNCTION__, 'OK', array ($added_count, $updated_count, $error_count));
851 }
852
853 function addBulkPorts ()
854 {
855 setFuncMessages (__FUNCTION__, array ('OK' => 82));
856 assertStringArg ('port_name', TRUE);
857 assertStringArg ('port_label', TRUE);
858
859 $object_id = getBypassValue();
860 $port_name = $_REQUEST['port_name'];
861 $port_type_id = genericAssertion ('port_type_id', 'string');
862 $port_label = $_REQUEST['port_label'];
863 $port_numbering_start = genericAssertion ('port_numbering_start', 'unsigned');
864 $port_numbering_count = genericAssertion ('port_numbering_count', 'natural');
865
866 $added_count = $error_count = 0;
867 if (strrpos ($port_name, '%u') === FALSE)
868 $port_name .= '%u';
869 if (strrpos ($port_label, '%u') === FALSE)
870 $port_label .= '%u';
871 for ($i = 0, $c = $port_numbering_start; $i < $port_numbering_count; $i++, $c++)
872 {
873 commitAddPort ($object_id, @sprintf ($port_name, $c), $port_type_id, @sprintf ($port_label, $c), '');
874 $added_count++;
875 }
876 showFuncMessage (__FUNCTION__, 'OK', array ($added_count, $error_count));
877 }
878
879 function updIPAllocation ()
880 {
881 setFuncMessages (__FUNCTION__, array ('OK' => 51));
882 $ip_bin = assertIPArg ('ip');
883 assertStringArg ('bond_name', TRUE);
884 updateIPBond
885 (
886 $ip_bin,
887 genericAssertion ('object_id', 'natural'),
888 $_REQUEST['bond_name'],
889 genericAssertion ('bond_type', 'enum/alloc_type')
890 );
891 showFuncMessage (__FUNCTION__, 'OK');
892 return buildRedirectURL (NULL, NULL, array ('hl_ip' => ip_format ($ip_bin)));
893 }
894
895 function delIPAllocation ()
896 {
897 setFuncMessages (__FUNCTION__, array ('OK' => 49));
898 unbindIPFromObject (genericAssertion ('ip', 'inet'), genericAssertion ('object_id', 'natural'));
899 showFuncMessage (__FUNCTION__, 'OK');
900 }
901
902 function addIPAllocation ()
903 {
904 setFuncMessages (__FUNCTION__, array ('OK' => 48, 'ERR1' => 170));
905 $ip_bin = assertIPArg ('ip');
906 $alloc_type = genericAssertion ('bond_type', 'enum/alloc_type');
907
908 // check if address is alread allocated
909 $address = getIPAddress($ip_bin);
910
911 if(!empty($address['allocs']) && ( ($address['allocs'][0]['type'] != 'shared') || ($alloc_type != 'shared') ) )
912 showWarning("IP ".ip_format($ip_bin)." already in use by ".$address['allocs'][0]['object_name']." - ".$address['allocs'][0]['name']);
913
914 if (getConfigVar ('IPV4_JAYWALK') != 'yes' && NULL === getIPAddressNetworkId ($ip_bin))
915 {
916 showFuncMessage (__FUNCTION__, 'ERR1', array (ip_format ($ip_bin)));
917 return;
918 }
919
920 if($address['reserved'] && $address['name'] != '')
921 {
922 showWarning("IP ".ip_format($ip_bin)." reservation \"".$address['name']."\" is removed");
923 //TODO ask to take reserved IP or not !
924 }
925
926 bindIPToObject
927 (
928 $ip_bin,
929 genericAssertion ('object_id', 'natural'),
930 genericAssertion ('bond_name', 'string0'),
931 $alloc_type
932 );
933
934 showFuncMessage (__FUNCTION__, 'OK');
935 return buildRedirectURL (NULL, NULL, array ('hl_ip' => ip_format ($ip_bin)));
936 }
937
938 function addIPv4Prefix ()
939 {
940 global $sic;
941 $vlan_ck = empty ($sic['vlan_ck']) ? NULL : genericAssertion ('vlan_ck', 'uint-vlan1');
942 try
943 {
944 $net_id = createIPv4Prefix
945 (
946 genericAssertion ('range', 'string'),
947 genericAssertion ('name', 'string0'),
948 isCheckSet ('is_connected'),
949 genericAssertion ('taglist', 'array0')
950 );
951 }
952 catch (InvalidRequestArgException $irae)
953 {
954 throw $irae;
955 }
956 catch (InvalidArgException $iae)
957 {
958 throw $iae->newIRAE();
959 }
960 $net_cell = spotEntity ('ipv4net', $net_id);
961 if (isset ($vlan_ck))
962 {
963 if (considerConfiguredConstraint ($net_cell, 'VLANNET_LISTSRC'))
964 commitSupplementVLANIPv4 ($vlan_ck, $net_id);
965 else
966 showError ("VLAN binding to network " . mkCellA ($net_cell) . " is restricted in config");
967 }
968 showSuccess ('IP network ' . mkCellA ($net_cell) . ' has been created');
969 }
970
971 function addIPv6Prefix ()
972 {
973 global $sic;
974 $vlan_ck = empty ($sic['vlan_ck']) ? NULL : genericAssertion ('vlan_ck', 'uint-vlan1');
975 try
976 {
977 $net_id = createIPv6Prefix
978 (
979 genericAssertion ('range', 'string'),
980 genericAssertion ('name', 'string0'),
981 isCheckSet ('is_connected'),
982 genericAssertion ('taglist', 'array0')
983 );
984 }
985 catch (InvalidRequestArgException $irae)
986 {
987 throw $irae;
988 }
989 catch (InvalidArgException $iae)
990 {
991 throw $iae->newIRAE();
992 }
993 $net_cell = spotEntity ('ipv6net', $net_id);
994 if (isset ($vlan_ck))
995 {
996 if (considerConfiguredConstraint ($net_cell, 'VLANNET_LISTSRC'))
997 commitSupplementVLANIPv6 ($vlan_ck, $net_id);
998 else
999 showError ("VLAN binding to network " . mkCellA ($net_cell) . " is restricted in config");
1000 }
1001 showSuccess ('IP network ' . mkCellA ($net_cell) . ' has been created');
1002 }
1003
1004 function delIPv4Prefix ()
1005 {
1006 setFuncMessages (__FUNCTION__, array ('OK' => 49));
1007 $netinfo = spotEntity ('ipv4net', genericAssertion ('id', 'natural'));
1008 loadIPAddrList ($netinfo);
1009 if (! isIPNetworkEmpty ($netinfo))
1010 {
1011 showError ("There are allocations within prefix, delete forbidden");
1012 return;
1013 }
1014 if (array_key_exists ($netinfo['ip_bin'], $netinfo['addrlist']))
1015 updateV4Address ($netinfo['ip_bin'], '', 'no');
1016 $last_ip = ip_last ($netinfo);
1017 if (array_key_exists ($last_ip, $netinfo['addrlist']))
1018 updateV4Address ($last_ip, '', 'no');
1019 destroyIPv4Prefix ($netinfo['id']);
1020 showFuncMessage (__FUNCTION__, 'OK');
1021 global $pageno;
1022 if ($pageno == 'ipv4net')
1023 return buildRedirectURL ('index', 'default');
1024 }
1025
1026 function delIPv6Prefix ()
1027 {
1028 setFuncMessages (__FUNCTION__, array ('OK' => 49));
1029 $netinfo = spotEntity ('ipv6net', genericAssertion ('id', 'natural'));
1030 loadIPAddrList ($netinfo);
1031 if (! isIPNetworkEmpty ($netinfo))
1032 {
1033 showError ("There are allocations within prefix, delete forbidden");
1034 return;
1035 }
1036 if (array_key_exists ($netinfo['ip_bin'], $netinfo['addrlist']))
1037 updateV6Address ($netinfo['ip_bin'], '', 'no');
1038 destroyIPv6Prefix ($netinfo['id']);
1039 showFuncMessage (__FUNCTION__, 'OK');
1040 global $pageno;
1041 if ($pageno == 'ipv6net')
1042 return buildRedirectURL ('index', 'default');
1043 }
1044
1045 function editAddress ()
1046 {
1047 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1048 assertStringArg ('name', TRUE);
1049 assertStringArg ('comment', TRUE);
1050 updateAddress
1051 (
1052 genericAssertion ('ip', 'inet'),
1053 $_REQUEST['name'],
1054 isCheckSet ('reserved', 'yesno'),
1055 $_REQUEST['comment']
1056 );
1057 showFuncMessage (__FUNCTION__, 'OK');
1058 }
1059
1060 function createUser ()
1061 {
1062 setFuncMessages (__FUNCTION__, array ('OK' => 5));
1063 assertStringArg ('username');
1064 assertStringArg ('realname', TRUE);
1065 assertStringArg ('password');
1066 $username = $_REQUEST['username'];
1067 $password = sha1 ($_REQUEST['password']);
1068 $user_id = commitCreateUserAccount ($username, $_REQUEST['realname'], $password);
1069 if (isset ($_REQUEST['taglist']))
1070 produceTagsForNewRecord ('user', $_REQUEST['taglist'], $user_id);
1071 showFuncMessage (__FUNCTION__, 'OK', array ($username));
1072 }
1073
1074 function updateUser ()
1075 {
1076 setFuncMessages (__FUNCTION__, array ('OK' => 6));
1077 $user_id = genericAssertion ('user_id', 'natural');
1078 $username = assertStringArg ('username');
1079 assertStringArg ('realname', TRUE);
1080 $new_password = assertStringArg ('password', TRUE);
1081 $userinfo = spotEntity ('user', $user_id);
1082 // Set new password only if provided.
1083 $new_password = $new_password != '' ? sha1 ($new_password) : $userinfo['user_password_hash'];
1084 commitUpdateUserAccount ($user_id, $username, $_REQUEST['realname'], $new_password);
1085 // if user account renaming is being performed, change key value in UserConfig table
1086 if ($userinfo['user_name'] !== $username)
1087 usePreparedUpdateBlade ('UserConfig', array ('user' => $username), array('user' => $userinfo['user_name']));
1088 showFuncMessage (__FUNCTION__, 'OK', array ($username));
1089 }
1090
1091 function deleteUser ()
1092 {
1093 setFuncMessages (__FUNCTION__, array ('OK' => 7));
1094 $user_id = genericAssertion ('id', 'natural');
1095 $userinfo = spotEntity ('user', $user_id);
1096 commitDeleteUserAccount ($user_id);
1097 showFuncMessage (__FUNCTION__, 'OK', array ($userinfo['user_name']));
1098 }
1099
1100 function supplementAttrMap ()
1101 {
1102 setFuncMessages (__FUNCTION__, array ('OK' => 48, 'ERR1' => 154));
1103 $attr_id = genericAssertion ('attr_id', 'natural');
1104 if (getAttrType ($attr_id) != 'dict')
1105 $chapter_id = NULL;
1106 else
1107 {
1108 try
1109 {
1110 $chapter_id = genericAssertion ('chapter_no', 'natural');
1111 }
1112 catch (InvalidRequestArgException $e)
1113 {
1114 showFuncMessage (__FUNCTION__, 'ERR1', array ('chapter not selected'));
1115 return;
1116 }
1117 }
1118 commitSupplementAttrMap ($attr_id, genericAssertion ('objtype_id', 'natural'), $chapter_id);
1119 showFuncMessage (__FUNCTION__, 'OK');
1120 }
1121
1122 function clearSticker ()
1123 {
1124 setFuncMessages (__FUNCTION__, array ('OK' => 49));
1125 $attr_id = genericAssertion ('attr_id', 'natural');
1126 if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $attr_id))))
1127 commitUpdateAttrValue (getBypassValue(), $attr_id);
1128 else
1129 {
1130 $oldvalues = getAttrValues (getBypassValue());
1131 showError ('Permission denied, "' . $oldvalues[$attr_id]['name'] . '" left unchanged');
1132 }
1133 }
1134
1135 // This function accepts rack data returned by amplifyCell(), validates and applies changes
1136 // supplied in $_REQUEST and returns resulting array. Only those changes are examined that
1137 // correspond to current rack ID.
1138 // 1st arg is rackdata, 2nd arg is unchecked state, 3rd arg is checked state.
1139 // If 4th arg is present, object_id fields will be updated accordingly to the new state.
1140 // The function returns TRUE if the DB was successfully changed, FALSE otherwise
1141 function processGridForm (&$rackData, $unchecked_state, $checked_state, $object_id = 0)
1142 {
1143 global $loclist, $dbxlink;
1144 $rack_id = $rackData['id'];
1145 $rack_name = $rackData['name'];
1146 $rackchanged = FALSE;
1147 $dbxlink->beginTransaction();
1148 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
1149 {
1150 for ($locidx = 0; $locidx < 3; $locidx++)
1151 {
1152 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
1153 continue;
1154 // detect a change
1155 $state = $rackData[$unit_no][$locidx]['state'];
1156 $newstate = isCheckSet ("atom_${rack_id}_${unit_no}_${locidx}") ? $checked_state : $unchecked_state;
1157 if ($state == $newstate)
1158 continue;
1159 $rackchanged = TRUE;
1160 // and validate
1161 $atom = $loclist[$locidx];
1162 // The only changes allowed are those introduced by checkbox grid.
1163 if
1164 (
1165 !($state == $checked_state && $newstate == $unchecked_state) &&
1166 !($state == $unchecked_state && $newstate == $checked_state)
1167 )
1168 {
1169 showError ("${rack_name}: Rack ID ${rack_id}, unit ${unit_no}, 'atom ${atom}', cannot change state from '${state}' to '${newstate}'");
1170 $dbxlink->rollBack();
1171 return FALSE;
1172 }
1173 // This code uses an unconditional DELETE followed by a conditional INSERT
1174 // rather than ON DUPLICATE KEY UPDATE.
1175 usePreparedDeleteBlade ('RackSpace', array ('rack_id' => $rack_id, 'unit_no' => $unit_no, 'atom' => $atom));
1176 if ($newstate != 'F')
1177 usePreparedInsertBlade ('RackSpace', array ('rack_id' => $rack_id, 'unit_no' => $unit_no, 'atom' => $atom, 'state' => $newstate));
1178 if ($newstate == 'T' && $object_id != 0)
1179 {
1180 // At this point respective row exists in RackSpace and has state set to "T".
1181 usePreparedUpdateBlade
1182 (
1183 'RackSpace',
1184 array ('object_id' => $object_id),
1185 array
1186 (
1187 'rack_id' => $rack_id,
1188 'unit_no' => $unit_no,
1189 'atom' => $atom,
1190 )
1191 );
1192 $rackData[$unit_no][$locidx]['object_id'] = $object_id;
1193 }
1194 }
1195 }
1196 if ($rackchanged)
1197 {
1198 usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
1199 $dbxlink->commit();
1200 return TRUE;
1201 }
1202 $dbxlink->rollBack();
1203 return FALSE;
1204 }
1205
1206 function updateObjectAllocation ()
1207 {
1208 setFuncMessages (__FUNCTION__, array ('OK' => 63));
1209 global $remote_username;
1210 global $op;
1211 if (!isset ($_REQUEST['got_atoms']))
1212 {
1213 unset($_GET['page']);
1214 unset($_GET['tab']);
1215 unset($_GET['op']);
1216 unset($_POST['page']);
1217 unset($_POST['tab']);
1218 unset($_POST['op']);
1219 return buildRedirectURL (NULL, NULL, $_REQUEST);
1220 }
1221 $object_id = getBypassValue();
1222 $object = spotEntity ('object', $object_id);
1223 $changecnt = 0;
1224 // Get a list of rack ids that are parents of the object
1225 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
1226 $workingRacksData = array();
1227 foreach ($_REQUEST['rackmulti'] as $cand_id)
1228 {
1229 if (!isset ($workingRacksData[$cand_id]))
1230 {
1231 $rackData = spotEntity ('rack', $cand_id);
1232 amplifyCell ($rackData);
1233 $workingRacksData[$cand_id] = $rackData;
1234 }
1235 else
1236 $rackData = $workingRacksData[$cand_id];
1237 $is_ro = ! rackModificationPermitted ($rackData, $op, FALSE);
1238 // It's zero-U mounted to this rack on the form, but not in the DB. Mount it.
1239 if (isset($_REQUEST["zerou_${cand_id}"]) && !in_array($cand_id, $parentRacks))
1240 {
1241 if ($is_ro)
1242 continue;
1243 $changecnt++;
1244 commitLinkEntities ('rack', $cand_id, 'object', $object_id);
1245 }
1246 // It's not zero-U mounted to this rack on the form, but it is in the DB. Unmount it.
1247 if (!isset($_REQUEST["zerou_${cand_id}"]) && in_array($cand_id, $parentRacks))
1248 {
1249 if ($is_ro)
1250 continue;
1251 $changecnt++;
1252 commitUnlinkEntities ('rack', $cand_id, 'object', $object_id);
1253 }
1254 }
1255
1256 foreach (array_keys ($workingRacksData) as $key)
1257 applyObjectMountMask ($workingRacksData[$key], $object_id);
1258
1259 $oldMolecule = getMoleculeForObject ($object_id);
1260 foreach ($workingRacksData as $rack_id => $rackData)
1261 {
1262 $is_ro = ! rackModificationPermitted ($rackData, $op, FALSE);
1263 if ($is_ro || !processGridForm ($rackData, 'F', 'T', $object_id))
1264 continue;
1265 $changecnt++;
1266 // Reload our working copy after form processing.
1267 $rackData = spotEntity ('rack', $cand_id);
1268 amplifyCell ($rackData);
1269 applyObjectMountMask ($rackData, $object_id);
1270 $workingRacksData[$rack_id] = $rackData;
1271 }
1272 if ($changecnt)
1273 {
1274 // Log a record.
1275 $newMolecule = getMoleculeForObject ($object_id);
1276 usePreparedInsertBlade
1277 (
1278 'MountOperation',
1279 array
1280 (
1281 'object_id' => $object_id,
1282 'old_molecule_id' => count ($oldMolecule) ? createMolecule ($oldMolecule) : NULL,
1283 'new_molecule_id' => count ($newMolecule) ? createMolecule ($newMolecule) : NULL,
1284 'user_name' => $remote_username,
1285 'comment' => nullIfEmptyStr (genericAssertion ('comment', 'string0')),
1286 )
1287 );
1288 }
1289 showFuncMessage (__FUNCTION__, 'OK', array ($changecnt));
1290 }
1291
1292 function updateObject ()
1293 {
1294 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1295 $taglist = genericAssertion ('taglist', 'array0');
1296 genericAssertion ('num_attrs', 'unsigned');
1297 genericAssertion ('object_name', 'string0');
1298 genericAssertion ('object_label', 'string0');
1299 genericAssertion ('object_asset_no', 'string0');
1300 genericAssertion ('object_comment', 'string0');
1301 $object_type_id = genericAssertion ('object_type_id', 'natural');
1302 $object_id = getBypassValue();
1303
1304 global $dbxlink;
1305 $dbxlink->beginTransaction();
1306 commitUpdateObject
1307 (
1308 $object_id,
1309 $_REQUEST['object_name'],
1310 $_REQUEST['object_label'],
1311 isCheckSet ('object_has_problems', 'yesno'),
1312 $_REQUEST['object_asset_no'],
1313 $_REQUEST['object_comment']
1314 );
1315 updateObjectAttributes ($object_id);
1316 $object = spotEntity ('object', $object_id);
1317 if ($object_type_id != $object['objtype_id'])
1318 {
1319 if (! array_key_exists ($object_type_id, getObjectTypeChangeOptions ($object_id)))
1320 throw new InvalidRequestArgException ('new type_id', $object_type_id, 'incompatible with requested attribute values');
1321 usePreparedUpdateBlade ('Object', array ('objtype_id' => $object_type_id), array ('id' => $object_id));
1322 }
1323 // Invalidate thumb cache of all racks objects could occupy.
1324 foreach (getResidentRackIDs ($object_id) as $rack_id)
1325 usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
1326 $dbxlink->commit();
1327 rebuildTagChainForEntity ('object', $object_id, buildTagChainFromIds ($taglist), TRUE);
1328 showFuncMessage (__FUNCTION__, 'OK');
1329 }
1330
1331 // Used when updating an object, location or rack
1332 function updateObjectAttributes ($object_id)
1333 {
1334 $type_id = getObjectType ($object_id);
1335 $oldvalues = getAttrValues ($object_id);
1336 $num_attrs = genericAssertion ('num_attrs', 'unsigned');
1337 for ($i = 0; $i < $num_attrs; $i++)
1338 {
1339 $attr_id = genericAssertion ("${i}_attr_id", 'natural');
1340 if (! array_key_exists ($attr_id, $oldvalues))
1341 throw new InvalidRequestArgException ('attr_id', $attr_id, 'malformed request');
1342 $value = genericAssertion ("${i}_value", 'string0');
1343
1344 // If the object is a rack, certain attributes (height, sort_order) never normally
1345 // appear in this subset of the request arguments as they are processed elsewhere.
1346 if ($type_id == 1560 && ($attr_id == 27 || $attr_id == 29))
1347 throw new RackTablesError ('unexpected special meaning attr_id', RackTablesError::INTERNAL);
1348
1349 // Delete attribute and move on, when the field is empty or if the field
1350 // type is a dictionary and it is the "--NOT SET--" value of 0.
1351 if ($value == '' || ($oldvalues[$attr_id]['type'] == 'dict' && $value == 0))
1352 {
1353 if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $attr_id))))
1354 commitUpdateAttrValue ($object_id, $attr_id);
1355 else
1356 showError ('Permission denied, "' . $oldvalues[$attr_id]['name'] . '" left unchanged');
1357 continue;
1358 }
1359
1360 try
1361 {
1362 switch ($oldvalues[$attr_id]['type'])
1363 {
1364 case 'uint':
1365 genericAssertion ("${i}_value", 'unsigned');
1366 $oldvalue = $oldvalues[$attr_id]['value'];
1367 break;
1368 case 'float':
1369 genericAssertion ("${i}_value", 'decimal0');
1370 $oldvalue = $oldvalues[$attr_id]['value'];
1371 break;
1372 case 'string':
1373 // already checked above
1374 $oldvalue = $oldvalues[$attr_id]['value'];
1375 break;
1376 case 'date':
1377 $value = timestampFromDatetimestr (genericAssertion ("${i}_value", 'datetime'));
1378 $oldvalue = $oldvalues[$attr_id]['value'];
1379 break;
1380 case 'dict':
1381 // Not 'unsigned' as 0 is handled above.
1382 genericAssertion ("${i}_value", 'natural');
1383 $oldvalue = $oldvalues[$attr_id]['key'];
1384 break;
1385 default:
1386 throw new RackTablesError ('Unexpected attribute type', RackTablesError::INTERNAL);
1387 }
1388 }
1389 catch (InvalidRequestArgException $irae)
1390 {
1391 // The submitted form may include a number of changes hence the error message
1392 // must use same term as the form label (before the conversion it is the input name).
1393 throw new InvalidRequestArgException ($oldvalues[$attr_id]['name'], $irae->getValue(), $irae->getReason());
1394 }
1395 if ($value === $oldvalue) // ('' == 0), but ('' !== 0)
1396 continue;
1397 if (permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $attr_id))))
1398 commitUpdateAttrValue ($object_id, $attr_id, $value);
1399 else
1400 showError ('Permission denied, "' . $oldvalues[$attr_id]['name'] . '" left unchanged');
1401 }
1402 }
1403
1404 function addMultipleObjects()
1405 {
1406 $taglist = genericAssertion ('taglist', 'array0');
1407 $max = genericAssertion ('num_records', 'natural');
1408 for ($i = 0; $i < $max; $i++)
1409 {
1410 $tid = genericAssertion ("${i}_object_type_id", 'unsigned'); // 0 by default in the SELECT
1411 assertStringArg ("${i}_object_name", TRUE);
1412 assertStringArg ("${i}_object_label", TRUE);
1413 assertStringArg ("${i}_object_asset_no", TRUE);
1414 $name = $_REQUEST["${i}_object_name"];
1415
1416 if ($tid == 0)
1417 continue; // Just skip on intact SELECT.
1418 try
1419 {
1420 $object_id = commitAddObject
1421 (
1422 $name,
1423 $_REQUEST["${i}_object_label"],
1424 $tid,
1425 $_REQUEST["${i}_object_asset_no"],
1426 $taglist
1427 );
1428 showSuccess ('added object ' . mkCellA (spotEntity ('object', $object_id)));
1429 }
1430 catch (RTDatabaseError $e)
1431 {
1432 showError ("Error creating object '$name': " . $e->getMessage());
1433 }
1434 }
1435 }
1436
1437 function addLotOfObjects()
1438 {
1439 $taglist = genericAssertion ('taglist', 'array0');
1440 assertStringArg ('namelist', TRUE);
1441 $global_type_id = genericAssertion ('global_type_id', 'unsigned'); // 0 by default in the SELECT
1442 if ($global_type_id == 0 || $_REQUEST['namelist'] == '')
1443 {
1444 showError ('Incomplete form has been ignored. Cheers.');
1445 return;
1446 }
1447 foreach (textareaCooked ($_REQUEST['namelist']) as $name)
1448 try
1449 {
1450 $object_id = commitAddObject ($name, NULL, $global_type_id, '', $taglist);
1451 showSuccess ('added object ' . mkCellA (spotEntity ('object', $object_id)));
1452 }
1453 catch (RackTablesError $e)
1454 {
1455 showError ("Failed to add object '$name': " . $e->getMessage());
1456 }
1457 }
1458
1459 function linkObjects ()
1460 {
1461 commitLinkEntities
1462 (
1463 genericAssertion ('parent_entity_type', 'string'),
1464 genericAssertion ('parent_entity_id', 'natural'),
1465 genericAssertion ('child_entity_type', 'string'),
1466 genericAssertion ('child_entity_id', 'natural')
1467 );
1468 showSuccess ('Container set successfully');
1469 }
1470
1471 function deleteObject ()
1472 {
1473 setFuncMessages (__FUNCTION__, array ('OK' => 7));
1474 $oinfo = spotEntity ('object', genericAssertion ('object_id', 'natural'));
1475
1476 $racklist = getResidentRackIDs ($oinfo['id']);
1477 commitDeleteObject ($oinfo['id']);
1478 foreach ($racklist as $rack_id)
1479 usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
1480 showFuncMessage (__FUNCTION__, 'OK', array ($oinfo['dname']));
1481 }
1482
1483 function resetObject ()
1484 {
1485 setFuncMessages (__FUNCTION__, array ('OK' => 57));
1486 $racklist = getResidentRackIDs (getBypassValue());
1487 commitResetObject (getBypassValue());
1488 foreach ($racklist as $rack_id)
1489 usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
1490 showFuncMessage (__FUNCTION__, 'OK');
1491 }
1492
1493 function updateUI ()
1494 {
1495 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1496 $num_vars = genericAssertion ('num_vars', 'natural');
1497 try
1498 {
1499 for ($i = 0; $i < $num_vars; $i++)
1500 {
1501 assertStringArg ("${i}_varvalue", TRUE);
1502 $varname = genericAssertion ("${i}_varname", 'string');
1503 $varvalue = $_REQUEST["${i}_varvalue"];
1504 // If form value = value in DB, don't bother updating DB.
1505 if (isConfigVarChanged ($varname, $varvalue))
1506 setConfigVar ($varname, $varvalue);
1507 }
1508 }
1509 catch (InvalidArgException $iae)
1510 {
1511 throw $iae->newIRAE();
1512 }
1513 showFuncMessage (__FUNCTION__, 'OK');
1514 }
1515
1516 function saveMyPreferences ()
1517 {
1518 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1519 $num_vars = genericAssertion ('num_vars', 'natural');
1520
1521 for ($i = 0; $i < $num_vars; $i++)
1522 {
1523 assertStringArg ("${i}_varvalue", TRUE);
1524 $varname = genericAssertion ("${i}_varname", 'string');
1525 $varvalue = $_REQUEST["${i}_varvalue"];
1526
1527 // If form value = value in DB, don't bother updating DB
1528 if (!isConfigVarChanged($varname, $varvalue))
1529 continue;
1530 try
1531 {
1532 setUserConfigVar ($varname, $varvalue);
1533 }
1534 catch (InvalidArgException $iae)
1535 {
1536 throw $iae->newIRAE();
1537 }
1538 }
1539 showFuncMessage (__FUNCTION__, 'OK');
1540 }
1541
1542 function resetMyPreference ()
1543 {
1544 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1545 try
1546 {
1547 resetUserConfigVar (genericAssertion ('varname', 'string'));
1548 }
1549 catch (InvalidArgException $iae)
1550 {
1551 throw $iae->newIRAE();
1552 }
1553 showFuncMessage (__FUNCTION__, 'OK');
1554 }
1555
1556 // FIXME: Move the default values to dictionary.php and feed from there into
1557 // this function and the installer to avoid duplication.
1558 function resetUIConfig()
1559 {
1560 setFuncMessages (__FUNCTION__, array ('OK' => 57));
1561 $defaults = array
1562 (
1563 'MASSCOUNT' => '8',
1564 'MAXSELSIZE' => '30',
1565 'ROW_SCALE' => '2',
1566 'IPV4_ADDRS_PER_PAGE' => '256',
1567 'DEFAULT_RACK_HEIGHT' => '42',
1568 'DEFAULT_SLB_VS_PORT' => '',
1569 'DEFAULT_SLB_RS_PORT' => '',
1570 'DETECT_URLS' => 'no',
1571 'RACK_PRESELECT_THRESHOLD' => '1',
1572 'DEFAULT_IPV4_RS_INSERVICE' => 'no',
1573 'AUTOPORTS_CONFIG' => '4 = 1*33*kvm + 2*24*eth%u;15 = 1*446*kvm',
1574 'SHOW_EXPLICIT_TAGS' => 'yes',
1575 'SHOW_IMPLICIT_TAGS' => 'yes',
1576 'SHOW_AUTOMATIC_TAGS' => 'no',
1577 'DEFAULT_OBJECT_TYPE' => '4',
1578 'IPV4_AUTO_RELEASE' => '1',
1579 'SHOW_LAST_TAB' => 'yes',
1580 'EXT_IPV4_VIEW' => 'yes',
1581 'TREE_THRESHOLD' => '25',
1582 'IPV4_JAYWALK' => 'no',
1583 'ADDNEW_AT_TOP' => 'yes',
1584 'IPV4_TREE_SHOW_USAGE' => 'no',
1585 'PREVIEW_TEXT_MAXCHARS' => '10240',
1586 'PREVIEW_TEXT_ROWS' => '25',
1587 'PREVIEW_TEXT_COLS' => '80',
1588 'PREVIEW_IMAGE_MAXPXS' => '320',
1589 'VENDOR_SIEVE' => '',
1590 'IPV4LB_LISTSRC' => 'false',
1591 'IPV4OBJ_LISTSRC' => 'not ({$typeid_3} or {$typeid_9} or {$typeid_10} or {$typeid_11})',
1592 'IPV4NAT_LISTSRC' => '{$typeid_4} or {$typeid_7} or {$typeid_8} or {$typeid_798}',
1593 'ASSETWARN_LISTSRC' => '{$typeid_4} or {$typeid_7} or {$typeid_8}',
1594 'NAMEWARN_LISTSRC' => '{$typeid_4} or {$typeid_7} or {$typeid_8}',
1595 'RACKS_PER_ROW' => '12',
1596 'FILTER_PREDICATE_SIEVE' => '',
1597 'FILTER_DEFAULT_ANDOR' => 'and',
1598 'FILTER_SUGGEST_ANDOR' => 'yes',
1599 'FILTER_SUGGEST_TAGS' => 'yes',
1600 'FILTER_SUGGEST_PREDICATES' => 'yes',
1601 'FILTER_SUGGEST_EXTRA' => 'no',
1602 'DEFAULT_SNMP_COMMUNITY' => 'public',
1603 'IPV4_ENABLE_KNIGHT' => 'yes',
1604 'TAGS_TOPLIST_SIZE' => '50',
1605 'TAGS_QUICKLIST_SIZE' => '20',
1606 'TAGS_QUICKLIST_THRESHOLD' => '50',
1607 'ENABLE_MULTIPORT_FORM' => 'no',
1608 'DEFAULT_PORT_IIF_ID' => '1',
1609 'DEFAULT_PORT_OIF_IDS' => '1=24; 3=1078; 4=1077; 5=1079; 6=1080; 8=1082; 9=1084; 10=1588; 11=1668; 12=1589; 13=1590; 14=1591',
1610 'IPV4_TREE_RTR_AS_CELL' => 'no',
1611 'PROXIMITY_RANGE' => '0',
1612 'IPV4_TREE_SHOW_VLAN' => 'yes',
1613 'VLANSWITCH_LISTSRC' => '',
1614 'VLANNET_LISTSRC' => '',
1615 'DEFAULT_VDOM_ID' => '',
1616 'DEFAULT_VST_ID' => '',
1617 'STATIC_FILTER' => 'yes',
1618 '8021Q_DEPLOY_MINAGE' => '300',
1619 '8021Q_DEPLOY_MAXAGE' => '3600',
1620 '8021Q_DEPLOY_RETRY' => '10800',
1621 '8021Q_WRI_AFTER_CONFT_LISTSRC' => 'false',
1622 '8021Q_INSTANT_DEPLOY' => 'no',
1623 'CDP_RUNNERS_LISTSRC' => '',
1624 'LLDP_RUNNERS_LISTSRC' => '',
1625 'SHRINK_TAG_TREE_ON_CLICK' => 'yes',
1626 'MAX_UNFILTERED_ENTITIES' => '0',
1627 'SYNCDOMAIN_MAX_PROCESSES' => '0',
1628 'PORT_EXCLUSION_LISTSRC' => '{$typeid_3} or {$typeid_10} or {$typeid_11} or {$typeid_1505} or {$typeid_1506}',
1629 'FILTER_RACKLIST_BY_TAGS' => 'yes',
1630 'MGMT_PROTOS' => 'ssh: {$typeid_4}; telnet: {$typeid_8}',
1631 'SYNC_8021Q_LISTSRC' => '',
1632 'QUICK_LINK_PAGES' => 'depot,ipv4space,rackspace',
1633 'VIRTUAL_OBJ_LISTSRC' => '1504,1505,1506,1507',
1634 'DATETIME_ZONE' => 'UTC',
1635 'DATETIME_FORMAT' => '%Y-%m-%d',
1636 'DATEONLY_FORMAT' => '%Y-%m-%d',
1637 'SEARCH_DOMAINS' => '',
1638 '8021Q_EXTSYNC_LISTSRC' => 'false',
1639 '8021Q_MULTILINK_LISTSRC' => 'false',
1640 'REVERSED_RACKS_LISTSRC' => 'false',
1641 'NEAREST_RACKS_CHECKBOX' => 'yes',
1642 'SHOW_OBJECTTYPE' => 'yes',
1643 'IPV4_TREE_SHOW_UNALLOCATED' => 'yes',
1644 );
1645 foreach ($defaults as $name => $value)
1646 setConfigVar ($name, $value);
1647 callHook ('resetUIConfig_hook');
1648 showFuncMessage (__FUNCTION__, 'OK');
1649 }
1650
1651 // Add single record.
1652 function addRealServer ()
1653 {
1654 setFuncMessages (__FUNCTION__, array ('OK' => 48));
1655 addRStoRSPool
1656 (
1657 getBypassValue(),
1658 genericAssertion ('rsip', 'inet'),
1659 genericAssertion ('rsport', 'string0'),
1660 isCheckSet ('inservice', 'yesno'),
1661 genericAssertion ('rsconfig', 'string0'),
1662 genericAssertion ('comment', 'string0')
1663 );
1664 showFuncMessage (__FUNCTION__, 'OK');
1665 }
1666
1667 // Parse textarea submitted and try adding a real server for each line.
1668 function addRealServers ()
1669 {
1670 setFuncMessages (__FUNCTION__, array ('OK' => 37, 'ERR1' => 131));
1671 $format = genericAssertion ('format', 'string');
1672 $ngood = 0;
1673 // Keep in mind that the text will have HTML entities (namely '>') escaped.
1674 foreach (explode ("\n", dos2unix (genericAssertion ('rawtext', 'string'))) as $line)
1675 {
1676 if ($line == '')
1677 continue;
1678 $match = array ();
1679 switch ($format)
1680 {
1681 case 'ipvs_2': // address and port only
1682 if (!preg_match ('/^ -> ([0-9\.]+):([0-9]+) /', $line, $match))
1683 if (!preg_match ('/^ -> \[([0-9a-fA-F:]+)\]:([0-9]+) /', $line, $match))
1684 continue;
1685 addRStoRSPool (getBypassValue(), ip_parse ($match[1]), $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
1686 break;
1687 case 'ipvs_3': // address, port and weight
1688 if (!preg_match ('/^ -> ([0-9\.]+):([0-9]+) +[a-zA-Z]+ +([0-9]+) /', $line, $match))
1689 if (!preg_match ('/^ -> \[([0-9a-fA-F:]+)\]:([0-9]+) +[a-zA-Z]+ +([0-9]+) /', $line, $match))
1690 continue;
1691 addRStoRSPool (getBypassValue(), ip_parse ($match[1]), $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), 'weight ' . $match[3]);
1692 break;
1693 case 'ssv_2': // IP address and port
1694 if (!preg_match ('/^([0-9\.a-fA-F:]+) ([0-9]+)$/', $line, $match))
1695 continue;
1696 addRStoRSPool (getBypassValue(), ip_parse ($match[1]), $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
1697 break;
1698 case 'ssv_1': // IP address
1699 if (! $ip_bin = ip_checkparse ($line))
1700 continue;
1701 addRStoRSPool (getBypassValue(), $ip_bin, 0, getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
1702 break;
1703 default:
1704 showFuncMessage (__FUNCTION__, 'ERR1');
1705 return;
1706 }
1707 $ngood++;
1708 }
1709 showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
1710 }
1711
1712 function addVService ()
1713 {
1714 assertStringArg ('name', TRUE);
1715 $proto = genericAssertion ('proto', 'enum/ipproto');
1716 usePreparedInsertBlade
1717 (
1718 'IPv4VS',
1719 array
1720 (
1721 'vip' => genericAssertion ('vip', 'inet'),
1722 'vport' => $proto == 'MARK' ? NULL : genericAssertion ('vport', 'natural'),
1723 'proto' => $proto,
1724 'name' => nullIfEmptyStr ($_REQUEST['name']),
1725 'vsconfig' => nullIfEmptyStr (genericAssertion ('vsconfig', 'string0')),
1726 'rsconfig' => nullIfEmptyStr (genericAssertion ('rsconfig', 'string0')),
1727 )
1728 );
1729 $vs_id = lastInsertID();
1730 lastCreated ('ipv4vs', $vs_id);
1731 if (isset ($_REQUEST['taglist']))
1732 produceTagsForNewRecord ('ipv4vs', genericAssertion ('taglist', 'array0'), $vs_id);
1733 $vsinfo = spotEntity ('ipv4vs', $vs_id);
1734 showSuccess (mkCellA ($vsinfo) . ' created successfully');
1735 }
1736
1737 function addVSG ()
1738 {
1739 $name = assertStringArg ('name');
1740 usePreparedInsertBlade ('VS', array ('name' => $name));
1741 $vs_id = lastInsertID();
1742 lastCreated ('ipvs', $vs_id);
1743 if (isset ($_REQUEST['taglist']))
1744 produceTagsForNewRecord ('ipvs', $_REQUEST['taglist'], $vs_id);
1745 $vsinfo = spotEntity ('ipvs', $vs_id);
1746 showSuccess (mkCellA ($vsinfo) . ' created successfully');
1747 }
1748
1749 function deleteVService ()
1750 {
1751 setFuncMessages (__FUNCTION__, array ('OK' => 49));
1752 $vsinfo = spotEntity ('ipv4vs', genericAssertion ('vs_id', 'natural'));
1753 if ($vsinfo['refcnt'] != 0)
1754 {
1755 showError ("Could not delete linked virtual service");
1756 return;
1757 }
1758 commitDeleteVS ($vsinfo['id']);
1759 showFuncMessage (__FUNCTION__, 'OK');
1760 return buildRedirectURL ('ipv4slb', 'default');
1761 }
1762
1763 function deleteVS()
1764 {
1765 $vsinfo = spotEntity ('ipvs', genericAssertion ('vs_id', 'natural'));
1766 if (count (getTriplets ($vsinfo)) != 0)
1767 {
1768 showError ("Could not delete linked virtual service group");
1769 return;
1770 }
1771 commitDeleteVSG ($vsinfo['id']);
1772 showSuccess (formatEntityName ($vsinfo) . ' deleted');
1773 return buildRedirectURL ('ipv4slb', 'vs');
1774 }
1775
1776 function updateSLBDefConfig ()
1777 {
1778 setFuncMessages (__FUNCTION__, array ('OK' => 43));
1779 commitUpdateSLBDefConf
1780 (
1781 array
1782 (
1783 'vs' => genericAssertion ('vsconfig', 'string0'),
1784 'rs' => genericAssertion ('rsconfig', 'string0'),
1785 )
1786 );
1787 showFuncMessage (__FUNCTION__, 'OK');
1788 }
1789
1790 function updateRealServer ()
1791 {
1792 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1793 commitUpdateRS (
1794 genericAssertion ('rs_id', 'natural'),
1795 genericAssertion ('rsip', 'inet'),
1796 genericAssertion ('rsport', 'string0'),
1797 isCheckSet ('inservice', 'yesno'),
1798 genericAssertion ('rsconfig', 'string0'),
1799 genericAssertion ('comment', 'string0')
1800 );
1801 showFuncMessage (__FUNCTION__, 'OK');
1802 }
1803
1804 function updateVService ()
1805 {
1806 setFuncMessages (__FUNCTION__, array ('OK' => 51));
1807 $vs_id = getBypassValue();
1808 $taglist = genericAssertion ('taglist', 'array0');
1809 $proto = genericAssertion ('proto', 'enum/ipproto');
1810 genericAssertion ('vport', $proto == 'MARK' ? 'string0' : 'natural');
1811 assertStringArg ('name', TRUE);
1812 commitUpdateVS (
1813 $vs_id,
1814 genericAssertion ('vip', 'inet'),
1815 $_REQUEST['vport'],
1816 $proto,
1817 $_REQUEST['name'],
1818 genericAssertion ('vsconfig', 'string0'),
1819 genericAssertion ('rsconfig', 'string0')
1820 );
1821 rebuildTagChainForEntity ('ipvs', $vs_id, buildTagChainFromIds ($taglist), TRUE);
1822 showFuncMessage (__FUNCTION__, 'OK');
1823 }
1824
1825 function updateVS ()
1826 {
1827 $taglist = genericAssertion ('taglist', 'array0');
1828 $vs_id = genericAssertion ('vs_id', 'natural');
1829 $name = assertStringArg ('name');
1830 $vsconfig = nullIfEmptyStr (assertStringArg ('vsconfig', TRUE));
1831 $rsconfig = nullIfEmptyStr (assertStringArg ('rsconfig', TRUE));
1832
1833 usePreparedUpdateBlade ('VS', array ('name' => $name, 'vsconfig' => $vsconfig, 'rsconfig' => $rsconfig), array ('id' => $vs_id));
1834 rebuildTagChainForEntity ('ipvs', $vs_id, buildTagChainFromIds ($taglist), TRUE);
1835 showSuccess ("Service updated successfully");
1836 }
1837
1838 function addIPToVS()
1839 {
1840 $ip_bin = assertIPArg ('ip');
1841 $vsinfo = spotEntity ('ipvs', getBypassValue());
1842 amplifyCell ($vsinfo);
1843 $row = array ('vs_id' => $vsinfo['id'], 'vip' => $ip_bin, 'vsconfig' => NULL, 'rsconfig' => NULL);
1844 if ($vip = isVIPEnabled ($row, $vsinfo['vips']))
1845 {
1846 showError ("Service already contains IP " . formatVSIP ($vip));
1847 return;
1848 }
1849 usePreparedInsertBlade ('VSIPs', $row);
1850 showSuccess ("IP addded");
1851 }
1852
1853 function addPortToVS()
1854 {
1855 $proto = genericAssertion ('proto', 'enum/ipproto');
1856 $vport = genericAssertion ('port', 'unsigned');
1857 if ($proto == 'MARK')
1858 {
1859 if ($vport > 0xFFFFFFFF)
1860 {
1861 showError ("fwmark value is too large");
1862 return;
1863 }
1864 }
1865 else
1866 if ($vport == 0 || $vport >= 0xFFFF)
1867 {
1868 showError ("Invalid $proto port value");
1869 return;
1870 }
1871
1872 $vsinfo = spotEntity ('ipvs', getBypassValue());
1873 amplifyCell ($vsinfo);
1874 $row = array ('vs_id' => $vsinfo['id'], 'proto' => $proto, 'vport' => $vport, 'vsconfig' => NULL, 'rsconfig' => NULL);
1875 if ($port = isPortEnabled ($row, $vsinfo['ports']))
1876 {
1877 showError ("Service already contains port " . $port['proto'] . ' ' . $port['vport']);
1878 return;
1879 }
1880 usePreparedInsertBlade ('VSPorts', $row);
1881 showSuccess ("port addded");
1882 }
1883
1884 function updateIPInVS()
1885 {
1886 $vs_id = getBypassValue();
1887 $ip_bin = assertIPArg ('ip');
1888 $vsconfig = nullIfEmptyStr (assertStringArg ('vsconfig', TRUE));
1889 $rsconfig = nullIfEmptyStr (assertStringArg ('rsconfig', TRUE));
1890 if (usePreparedUpdateBlade ('VSIPs', array ('vsconfig' => $vsconfig, 'rsconfig' => $rsconfig), array ('vs_id' => $vs_id, 'vip' => $ip_bin)))
1891 showSuccess ("IP configuration updated");
1892 else
1893 showNotice ("Nothing changed");
1894 }
1895
1896 function updatePortInVS()
1897 {
1898 $vs_id = getBypassValue();
1899 $proto = genericAssertion ('proto', 'enum/ipproto');
1900 $vport = genericAssertion ('port', 'unsigned');
1901 $vsconfig = nullIfEmptyStr (assertStringArg ('vsconfig', TRUE));
1902 $rsconfig = nullIfEmptyStr (assertStringArg ('rsconfig', TRUE));
1903 if (usePreparedUpdateBlade ('VSPorts', array ('vsconfig' => $vsconfig, 'rsconfig' => $rsconfig), array ('vs_id' => $vs_id, 'proto' => $proto, 'vport' => $vport)))
1904 showSuccess ("Port configuration updated");
1905 else
1906 showNotice ("Nothing changed");
1907 }
1908
1909 function removeIPFromVS()
1910 {
1911 $vip = array ('vip' => assertIPArg ('ip'));
1912 $vsinfo = spotEntity ('ipvs', getBypassValue());
1913 amplifyCell ($vsinfo);
1914 $used = 0;
1915 foreach (getTriplets ($vsinfo) as $triplet)
1916 if (isVIPEnabled ($vip, $triplet['vips']))
1917 $used++;
1918 if (usePreparedDeleteBlade ('VSIPs', array ('vs_id' => $vsinfo['id']) + $vip))
1919 showSuccess ("IP removed" . ($used ? ", it was binded with $used SLBs" : ''));
1920 else
1921 showNotice ("Nothing changed");
1922 }
1923
1924 function removePortFromVS()
1925 {
1926 $port = array ('proto' => genericAssertion ('proto', 'enum/ipproto'), 'vport' => genericAssertion ('port', 'unsigned'));
1927 $vsinfo = spotEntity ('ipvs', getBypassValue());
1928 amplifyCell ($vsinfo);
1929 $used = 0;
1930 foreach (getTriplets ($vsinfo) as $triplet)
1931 if (isPortEnabled ($port, $triplet['ports']))
1932 $used++;
1933 if (usePreparedDeleteBlade ('VSPorts', array ('vs_id' => $vsinfo['id']) + $port))
1934 showSuccess ("Port removed" . ($used ? ", it was binded with $used SLBs" : ''));
1935 else
1936 showNotice ("Nothing changed");
1937 }
1938
1939 function updateTripletConfig()
1940 {
1941 global $op;
1942 $key_fields = array
1943 (
1944 'object_id' => genericAssertion ('object_id', 'natural'),
1945 'vs_id' => genericAssertion ('vs_id', 'natural'),
1946 'rspool_id' => genericAssertion ('rspool_id', 'natural'),
1947 );
1948 $config_fields = array
1949 (
1950 'vsconfig' => nullIfEmptyStr (assertStringArg ('vsconfig', TRUE)),
1951 'rsconfig' => nullIfEmptyStr (assertStringArg ('rsconfig', TRUE)),
1952 );
1953
1954 $vsinfo = spotEntity ('ipvs', $key_fields['vs_id']);
1955 amplifyCell ($vsinfo);
1956 $found = FALSE;
1957
1958 if ($op == 'updPort')
1959 {
1960 $table = 'VSEnabledPorts';
1961 $proto = assertStringArg ('proto');
1962 $vport = genericAssertion ('port', 'unsigned');
1963 $key_fields['proto'] = $proto;
1964 $key_fields['vport'] = $vport;
1965 $key = "Port $proto-$vport";
1966 // check if such port exists in VS
1967 foreach ($vsinfo['ports'] as $vs_port)
1968 if ($vs_port['proto'] == $proto && $vs_port['vport'] == $vport)
1969 {
1970 $found = TRUE;
1971 break;
1972 }
1973 }
1974 else
1975 {
1976 $table = 'VSEnabledIPs';
1977 $vip = assertIPArg ('vip');
1978 $config_fields['prio'] = nullIfEmptyStr (assertStringArg ('prio', TRUE));
1979 $key_fields['vip'] = $vip;
1980 $key = "IP " . ip_format ($vip);
1981 // check if such VIP exists in VS
1982 foreach ($vsinfo['vips'] as $vs_vip)
1983 if ($vs_vip['vip'] === $vip)
1984 {
1985 $found = TRUE;
1986 break;
1987 }
1988 }
1989 if (! $found)
1990 {
1991 showError ("$key not found in VS");
1992 return;
1993 }
1994
1995 $nchanged = 0;
1996 if (! isCheckSet ('enabled'))
1997 {
1998 if ($nchanged += usePreparedDeleteBlade ($table, $key_fields))
1999 {
2000 showSuccess ("$key disabled");
2001 return;
2002 }
2003 }
2004 else
2005 {
2006 global $dbxlink;
2007 $dbxlink->beginTransaction();
2008 $q = "SELECT * FROM $table WHERE";
2009 $sep = '';
2010 $params = array();
2011 foreach ($key_fields as $field => $value)
2012 {
2013 $q .= " $sep $field = ?";
2014 $params[] = $value;
2015 $sep = 'AND';
2016 }
2017 $result = usePreparedSelectBlade ("$q FOR UPDATE", $params);
2018 $row = $result->fetch (PDO::FETCH_ASSOC);
2019 unset ($result);
2020 if ($row)
2021 {
2022 if ($nchanged += usePreparedUpdateBlade ($table, $config_fields, $key_fields))
2023 showSuccess ("$key config updated");
2024 }
2025 else
2026 {
2027 if (
2028 $nchanged += ($table == 'VSEnabledIPs' ?
2029 addSLBIPLink ($key_fields + $config_fields) :
2030 addSLBPortLink ($key_fields + $config_fields)
2031 )
2032 )
2033 showSuccess ("$key enabled");
2034 }
2035 $dbxlink->commit();
2036 }
2037 if (! $nchanged)
2038 showNotice ("No changes made");
2039 }
2040
2041 function removeTriplet()
2042 {
2043 $key_fields = array
2044 (
2045 'object_id' => genericAssertion ('object_id', 'natural'),
2046 'vs_id' => genericAssertion ('vs_id', 'natural'),
2047 'rspool_id' => genericAssertion ('rspool_id', 'natural'),
2048 );
2049
2050 global $dbxlink;
2051 $dbxlink->beginTransaction();
2052 usePreparedDeleteBlade ('VSEnabledIPs', $key_fields);
2053 usePreparedDeleteBlade ('VSEnabledPorts', $key_fields);
2054 $dbxlink->commit();
2055 showSuccess ('Triplet deleted');
2056 }
2057
2058 function createTriplet()
2059 {
2060 global $dbxlink;
2061 $object_id = genericAssertion ('object_id', 'natural');
2062 $vs_id = genericAssertion ('vs_id', 'natural');
2063 $rspool_id = genericAssertion ('rspool_id', 'natural');
2064 $vips = genericAssertion ('enabled_vips', 'array0');
2065 $ports = genericAssertion ('enabled_ports', 'array0');
2066
2067 $vsinfo = spotEntity ('ipvs', $vs_id);
2068 amplifyCell ($vsinfo);
2069 try
2070 {
2071 $dbxlink->beginTransaction();
2072 foreach ($vsinfo['vips'] as $vip)
2073 if (in_array (ip_format ($vip['vip']), $vips))
2074 addSLBIPLink (array ('object_id' => $object_id, 'vs_id' => $vs_id, 'rspool_id' => $rspool_id, 'vip' => $vip['vip']));
2075 foreach ($vsinfo['ports'] as $port)
2076 if (in_array($port['proto'] . '-' . $port['vport'], $ports))
2077 addSLBPortLink (array ('object_id' => $object_id, 'vs_id' => $vs_id, 'rspool_id' => $rspool_id, 'proto' => $port['proto'], 'vport' => $port['vport']));
2078 $dbxlink->commit();
2079 }
2080 catch (RTDatabaseError $e)
2081 {
2082 $dbxlink->rollBack();
2083 throw $e;
2084 }
2085 showSuccess ("SLB triplet created");
2086 }
2087
2088 function addLoadBalancer ()
2089 {
2090 setFuncMessages (__FUNCTION__, array ('OK' => 48));
2091 assertStringArg ('prio', TRUE);
2092
2093 addLBtoRSPool (
2094 genericAssertion ('pool_id', 'natural'),
2095 genericAssertion ('object_id', 'natural'),
2096 genericAssertion ('vs_id', 'natural'),
2097 genericAssertion ('vsconfig', 'string0'),
2098 genericAssertion ('rsconfig', 'string0'),
2099 $_REQUEST['prio']
2100 );
2101 showFuncMessage (__FUNCTION__, 'OK');
2102 }
2103
2104 function addRSPool ()
2105 {
2106 assertStringArg ('name');
2107 $pool_id = commitCreateRSPool
2108 (
2109 $_REQUEST['name'],
2110 genericAssertion ('vsconfig', 'string0'),
2111 genericAssertion ('rsconfig', 'string0'),
2112 isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array()
2113 );
2114 showSuccess ('RS pool ' . mkA ($_REQUEST['name'], 'ipv4rspool', $pool_id) . ' created successfully');
2115 }
2116
2117 function deleteRSPool ()
2118 {
2119 setFuncMessages (__FUNCTION__, array ('OK' => 49));
2120 $poolinfo = spotEntity ('ipv4rspool', genericAssertion ('pool_id', 'natural'));
2121 if ($poolinfo['refcnt'] != 0)
2122 {
2123 showError ("Could not delete linked RS pool");
2124 return;
2125 }
2126 commitDeleteRSPool ($poolinfo['id']);
2127 showFuncMessage (__FUNCTION__, 'OK');
2128 return buildRedirectURL ('ipv4slb', 'rspools');
2129 }
2130
2131 function importPTRData ()
2132 {
2133 setFuncMessages (__FUNCTION__, array ('OK' => 26, 'ERR' => 141));
2134 $net = spotEntity ('ipv4net', getBypassValue());
2135 $addrcount = genericAssertion ('addrcount', 'natural');
2136 $nbad = $ngood = 0;
2137 for ($i = 1; $i <= $addrcount; $i++)
2138 {
2139 $inputname = "import_${i}";
2140 if (! isCheckSet ($inputname))
2141 continue;
2142 $ip_bin = assertIPv4Arg ("addr_${i}");
2143 assertStringArg ("descr_${i}", TRUE);
2144 assertStringArg ("rsvd_${i}");
2145 // Non-existent addresses will not have this argument set in request.
2146 $rsvd = 'no';
2147 if ($_REQUEST["rsvd_${i}"] == 'yes')
2148 $rsvd = 'yes';
2149 try
2150 {
2151 if (! ip_in_range ($ip_bin, $net))
2152 throw new InvalidArgException ('ip_bin', $ip_bin);
2153 updateAddress ($ip_bin, $_REQUEST["descr_${i}"], $rsvd);
2154 $ngood++;
2155 }
2156 catch (RackTablesError $e)
2157 {
2158 $nbad++;
2159 }
2160 }
2161 if (!$nbad)
2162 showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
2163 else
2164 showFuncMessage (__FUNCTION__, 'ERR', array ($nbad, $ngood));
2165 }
2166
2167 function generateAutoPorts ()
2168 {
2169 setFuncMessages (__FUNCTION__, array ('OK' => 21));
2170 $object = spotEntity ('object', getBypassValue());
2171 executeAutoPorts ($object['id']);
2172 showFuncMessage (__FUNCTION__, 'OK');
2173 return buildRedirectURL (NULL, 'ports');
2174 }
2175
2176 function updateTag ()
2177 {
2178 try
2179 {
2180 commitUpdateTag
2181 (
2182 genericAssertion ('tag_id', 'natural'),
2183 genericAssertion ('tag_name', 'tag'),
2184 genericAssertion ('parent_id', 'unsigned'),
2185 genericAssertion ('is_assignable', 'enum/yesno'),
2186 genericAssertion ('color', 'htmlcolor0')
2187 );
2188 }
2189 catch (InvalidArgException $iae)
2190 {
2191 throw $iae->newIRAE();
2192 }
2193 showSuccess ('Tag updated successfully');
2194 }
2195
2196 function saveEntityTags ()
2197 {
2198 setFuncMessages (__FUNCTION__, array ('OK' => 43));
2199 $realm = etypeByPageno();
2200 $entity_id = getBypassValue();
2201 $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
2202 rebuildTagChainForEntity ($realm, $entity_id, buildTagChainFromIds ($taglist), TRUE);
2203 showFuncMessage (__FUNCTION__, 'OK');
2204 }
2205
2206 function rollTags ()
2207 {
2208 setFuncMessages (__FUNCTION__, array ('OK' => 67, 'ERR' => 149));
2209 if (genericAssertion ('sum', 'string0') != genericAssertion ('realsum', 'natural'))
2210 {
2211 showFuncMessage (__FUNCTION__, 'ERR');
2212 return;
2213 }
2214 // Even if the user requested an empty tag list, don't bail out, but process existing
2215 // tag chains with "zero" extra. This will make sure that the stuff processed will
2216 // have its chains refined to "normal" form.
2217 $extratags = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
2218 $n_ok = 0;
2219 // Minimizing the extra chain early, so that tag rebuilder doesn't have to
2220 // filter out the same tag again and again. It will have own noise to cancel.
2221 $extrachain = getExplicitTagsOnly (buildTagChainFromIds ($extratags));
2222 foreach (listCells ('rack', getBypassValue()) as $rack)
2223 {
2224 if (rebuildTagChainForEntity ('rack', $rack['id'], $extrachain))
2225 $n_ok++;
2226 amplifyCell ($rack);
2227 foreach ($rack['mountedObjects'] as $object_id)
2228 if (rebuildTagChainForEntity ('object', $object_id, $extrachain))
2229 $n_ok++;
2230 }
2231 showFuncMessage (__FUNCTION__, 'OK', array ($n_ok));
2232 }
2233
2234 function changeMyPassword ()
2235 {
2236 setFuncMessages (__FUNCTION__, array ('OK' => 51, 'ERR1' => 150, 'ERR2' => 151, 'ERR3' => 152));
2237 global $remote_username, $user_auth_src;
2238 if ($user_auth_src != 'database')
2239 {
2240 showFuncMessage (__FUNCTION__, 'ERR1');
2241 return;
2242 }
2243 assertStringArg ('oldpassword');
2244 assertStringArg ('newpassword1');
2245 assertStringArg ('newpassword2');
2246 $remote_userid = getUserIDByUsername ($remote_username);
2247 $userinfo = spotEntity ('user', $remote_userid);
2248 if ($userinfo['user_password_hash'] != sha1 ($_REQUEST['oldpassword']))
2249 {
2250 showFuncMessage (__FUNCTION__, 'ERR2');
2251 return;
2252 }
2253 if ($_REQUEST['newpassword1'] != $_REQUEST['newpassword2'])
2254 {
2255 showFuncMessage (__FUNCTION__, 'ERR3');
2256 return;
2257 }
2258 commitUpdateUserAccount ($remote_userid, $userinfo['user_name'], $userinfo['user_realname'], sha1 ($_REQUEST['newpassword1']));
2259 showFuncMessage (__FUNCTION__, 'OK');
2260 }
2261
2262 function saveRackCode ()
2263 {
2264 setFuncMessages (__FUNCTION__, array ('OK' => 43, 'ERR1' => 154));
2265 assertStringArg ('rackcode');
2266 // For the test to succeed, unescape LFs, strip CRs.
2267 $newcode = dos2unix ($_REQUEST['rackcode']);
2268 $parseTree = getRackCode ($newcode);
2269 if ($parseTree['result'] != 'ACK')
2270 {
2271 showFuncMessage (__FUNCTION__, 'ERR1', array ($parseTree['load']));
2272 return;
2273 }
2274 saveScript ('RackCode', $newcode);
2275 saveScript ('RackCodeCache', base64_encode (serialize ($parseTree)));
2276 showFuncMessage (__FUNCTION__, 'OK');
2277 }
2278
2279 function submitSLBConfig ()
2280 {
2281 showNotice ("You should redefine submitSLBConfig ophandler in your local extension to install SLB config");
2282 }
2283
2284 function addLocation ()
2285 {
2286 setFuncMessages (__FUNCTION__, array ('OK' => 5));
2287 assertStringArg ('name');
2288
2289 $location_id = commitAddObject ($_REQUEST['name'], NULL, 1562, NULL);
2290 if (0 != $parent_id = genericAssertion ('parent_id', 'unsigned'))
2291 commitLinkEntities ('location', $parent_id, 'location', $location_id);
2292 showSuccess ('added location ' . mkA ($_REQUEST['name'], 'location', $location_id));
2293 }
2294
2295 // This function is used by two forms:
2296 // - renderEditLocationForm - all attributes may be modified
2297 // - renderRackspaceLocationEditor - only the name and parent may be modified
2298 function updateLocation ()
2299 {
2300 setFuncMessages (__FUNCTION__, array ('OK' => 6));
2301 global $pageno;
2302 $location_id = genericAssertion ('location_id', 'natural');
2303 $parent_id = genericAssertion ('parent_id', 'unsigned');
2304 assertStringArg ('name');
2305
2306 if ($pageno == 'location')
2307 {
2308 $taglist = genericAssertion ('taglist', 'array0');
2309 $has_problems = isCheckSet ('has_problems', 'yesno');
2310 assertStringArg ('comment', TRUE);
2311 commitUpdateObject ($location_id, $_REQUEST['name'], NULL, $has_problems, NULL, $_REQUEST['comment']);
2312 updateObjectAttributes ($location_id);
2313 rebuildTagChainForEntity ('location', $location_id, buildTagChainFromIds ($taglist), TRUE);
2314 }
2315 else
2316 commitRenameObject ($location_id, $_REQUEST['name']);
2317
2318 $locationData = spotEntity ('location', $location_id);
2319
2320 // parent_id was submitted, but no link exists - create it
2321 if ($parent_id > 0 && !$locationData['parent_id'])
2322 commitLinkEntities ('location', $parent_id, 'location', $location_id);
2323
2324 // parent_id was submitted, but it doesn't match the existing link - update it
2325 if ($parent_id > 0 && $parent_id != $locationData['parent_id'])
2326 commitUpdateEntityLink
2327 (
2328 'location', $locationData['parent_id'], 'location', $location_id,
2329 'location', $parent_id, 'location', $location_id
2330 );
2331
2332 // no parent_id was submitted, but a link exists - delete it
2333 if ($parent_id == 0 && $locationData['parent_id'])
2334 commitUnlinkEntities ('location', $locationData['parent_id'], 'location', $location_id);
2335
2336 showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
2337 }
2338
2339 function deleteLocation ()
2340 {
2341 setFuncMessages (__FUNCTION__, array ('OK' => 7, 'ERR1' => 206));
2342 $location_id = genericAssertion ('location_id', 'natural');
2343 $locationData = spotEntity ('location', $location_id);
2344 amplifyCell ($locationData);
2345 if (count ($locationData['locations']) || count ($locationData['rows']))
2346 {
2347 showFuncMessage (__FUNCTION__, 'ERR1', array ($locationData['name']));
2348 return;
2349 }
2350 releaseFiles ('location', $location_id);
2351 destroyTagsForEntity ('location', $location_id);
2352 commitDeleteObject ($location_id);
2353 showFuncMessage (__FUNCTION__, 'OK', array ($locationData['name']));
2354 return buildRedirectURL ('rackspace', 'editlocations');
2355 }
2356
2357 function addRow ()
2358 {
2359 setFuncMessages (__FUNCTION__, array ('OK' => 5));
2360 $location_id = genericAssertion ('location_id', 'unsigned');
2361 assertStringArg ('name');
2362 $row_id = commitAddObject ($_REQUEST['name'], NULL, 1561, NULL);
2363 if ($location_id)
2364 commitLinkEntities ('location', $location_id, 'row', $row_id);
2365 showSuccess ('added row ' . mkA ($_REQUEST['name'], 'row', $row_id));
2366 }
2367
2368 // This function is used by two forms:
2369 // - renderEditRowForm - all attributes may be modified
2370 // - renderRackspaceRowEditor - only the name and location may be modified
2371 function updateRow ()
2372 {
2373 setFuncMessages (__FUNCTION__, array ('OK' => 6));
2374 $row_id = genericAssertion ('row_id', 'natural');
2375 $location_id = genericAssertion ('location_id', 'unsigned');
2376 assertStringArg ('name');
2377
2378 commitUpdateObject ($row_id, $_REQUEST['name'], NULL, 'no', NULL, NULL);
2379
2380 global $pageno;
2381 if ($pageno == 'row')
2382 updateObjectAttributes ($row_id);
2383
2384 $rowData = spotEntity ('row', $row_id);
2385
2386 // location_id was submitted, but no link exists - create it
2387 if ($location_id > 0 && !$rowData['location_id'])
2388 commitLinkEntities ('location', $location_id, 'row', $row_id);
2389
2390 // location_id was submitted, but it doesn't match the existing link - update it
2391 if ($location_id > 0 && $location_id != $rowData['location_id'])
2392 commitUpdateEntityLink
2393 (
2394 'location', $rowData['location_id'], 'row', $row_id,
2395 'location', $location_id, 'row', $row_id
2396 );
2397
2398 // no parent_id was submitted, but a link exists - delete it
2399 if ($location_id == 0 && $rowData['location_id'])
2400 commitUnlinkEntities ('location', $rowData['location_id'], 'row', $row_id);
2401
2402 showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
2403 }
2404
2405 function deleteRow ()
2406 {
2407 setFuncMessages (__FUNCTION__, array ('OK' => 7, 'UMOUNT' => 58));
2408 $row_id = genericAssertion ('row_id', 'natural');
2409 $rowData = spotEntity ('row', $row_id);
2410 $unmounted = getRowMountsCount ($row_id);
2411 commitDeleteRow ($row_id);
2412 if ($unmounted)
2413 showFuncMessage (__FUNCTION__, 'UMOUNT', array ($unmounted));
2414 showFuncMessage (__FUNCTION__, 'OK', array ($rowData['name']));
2415 return buildRedirectURL ('rackspace', 'editrows');
2416 }
2417
2418 function addRack ()
2419 {
2420 setFuncMessages (__FUNCTION__, array ('ERR2' => 172));
2421 $taglist = genericAssertion ('taglist', 'array0');
2422 $row_id = getBypassValue();
2423
2424 // The new rack(s) should be placed on the bottom of the list, sort-wise
2425 $rowInfo = getRowInfo ($row_id);
2426 $sort_order = $rowInfo['count']+1;
2427
2428 switch (genericAssertion ('mode', 'string'))
2429 {
2430 case 'one':
2431 assertStringArg ('name');
2432 $height = genericAssertion ('height1', 'natural');
2433 assertStringArg ('asset_no', TRUE);
2434 $rack_id = commitAddObject ($_REQUEST['name'], NULL, 1560, $_REQUEST['asset_no'], $taglist);
2435
2436 // Set the height and sort order
2437 commitUpdateAttrValue ($rack_id, 27, $height);
2438 commitUpdateAttrValue ($rack_id, 29, $sort_order);
2439
2440 // Link it to the row
2441 commitLinkEntities ('row', $row_id, 'rack', $rack_id);
2442 showSuccess ('added ' . mkCellA (spotEntity ('rack', $rack_id)));
2443 break;
2444 case 'many':
2445 $height = genericAssertion ('height2', 'natural');
2446 assertStringArg ('names', TRUE);
2447 foreach (textareaCooked ($_REQUEST['names']) as $cname)
2448 {
2449 $rack_id = commitAddObject ($cname, NULL, 1560, NULL, $taglist);
2450
2451 // Set the height and sort order
2452 commitUpdateAttrValue ($rack_id, 27, $height);
2453 commitUpdateAttrValue ($rack_id, 29, $sort_order);
2454 $sort_order++;
2455
2456 // Link it to the row
2457 commitLinkEntities ('row', $row_id, 'rack', $rack_id);
2458 showSuccess ('added ' . mkCellA (spotEntity ('rack', $rack_id)));
2459 }
2460 break;
2461 default:
2462 showFuncMessage (__FUNCTION__, 'ERR2');
2463 }
2464 }
2465
2466 function updateRack ()
2467 {
2468 setFuncMessages (__FUNCTION__, array ('OK' => 6));
2469 $row_id = genericAssertion ('row_id', 'natural');
2470 assertStringArg ('name');
2471 $height = genericAssertion ('height', 'natural');
2472 assertStringArg ('asset_no', TRUE);
2473 assertStringArg ('comment', TRUE);
2474 $taglist = genericAssertion ('taglist', 'array0');
2475 $rack_id = getBypassValue();
2476 usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
2477 commitUpdateRack
2478 (
2479 $rack_id,
2480 $row_id,
2481 $_REQUEST['name'],
2482 $height,
2483 isCheckSet ('has_problems', 'yesno'),
2484 $_REQUEST['asset_no'],
2485 $_REQUEST['comment']
2486 );
2487 updateObjectAttributes ($rack_id);
2488 rebuildTagChainForEntity ('rack', $rack_id, buildTagChainFromIds ($taglist), TRUE);
2489 showFuncMessage (__FUNCTION__, 'OK', array ($_REQUEST['name']));
2490 }
2491
2492 function deleteRack ()
2493 {
2494 setFuncMessages (__FUNCTION__, array ('OK' => 7, 'ERR1' => 206));
2495 $rackData = spotEntity ('rack', getBypassValue());
2496 amplifyCell ($rackData);
2497 if (!$rackData['isDeletable'])
2498 {
2499 showFuncMessage (__FUNCTION__, 'ERR1');
2500 return;
2501 }
2502 commitDeleteRack ($rackData['id']);
2503 showFuncMessage (__FUNCTION__, 'OK', array (formatEntityName ($rackData)));
2504 return buildRedirectURL ('rackspace', 'default');
2505 }
2506
2507 function cleanRack ()
2508 {
2509 setFuncMessages (__FUNCTION__, array ('OK' => 58));
2510 $rack_id = getBypassValue();
2511 $unmounted = getRackMountsCount ($rack_id);
2512 commitCleanRack ($rack_id);
2513 showFuncMessage (__FUNCTION__, 'OK', array ($unmounted));
2514 }
2515
2516 function updateRackDesign ()
2517 {
2518 $rackData = spotEntity ('rack', getBypassValue());
2519 amplifyCell ($rackData);
2520 applyRackDesignMask($rackData);
2521 if (processGridForm ($rackData, 'A', 'F'))
2522 showSuccess ("Saved successfully");
2523 else
2524 showNotice ("Nothing saved");
2525 }
2526
2527 function updateRackProblems ()
2528 {
2529 $rackData = spotEntity ('rack', getBypassValue());
2530 amplifyCell ($rackData);
2531 applyRackProblemMask($rackData);
2532 if (processGridForm ($rackData, 'F', 'U'))
2533 showSuccess ("Saved successfully");
2534 else
2535 showNotice ("Nothing saved");
2536 }
2537
2538 function querySNMPData ()
2539 {
2540 $ver = genericAssertion ('ver', 'natural');
2541 $snmpsetup = array ();
2542 switch ($ver)
2543 {
2544 case 1:
2545 case 2:
2546 genericAssertion ('community', 'string');
2547 $snmpsetup['community'] = $_REQUEST['community'];
2548 break;
2549 case 3:
2550 assertStringArg ('sec_name');
2551 assertStringArg ('sec_level');
2552 assertStringArg ('auth_protocol');
2553 assertStringArg ('auth_passphrase', TRUE);
2554 assertStringArg ('priv_protocol');
2555 assertStringArg ('priv_passphrase', TRUE);
2556
2557 $snmpsetup['sec_name'] = $_REQUEST['sec_name'];
2558 $snmpsetup['sec_level'] = $_REQUEST['sec_level'];
2559 $snmpsetup['auth_protocol'] = $_REQUEST['auth_protocol'];
2560 $snmpsetup['auth_passphrase'] = $_REQUEST['auth_passphrase'];
2561 $snmpsetup['priv_protocol'] = $_REQUEST['priv_protocol'];
2562 $snmpsetup['priv_passphrase'] = $_REQUEST['priv_passphrase'];
2563 break;
2564 default:
2565 throw new InvalidRequestArgException ('ver', $ver);
2566 }
2567 $snmpsetup['version'] = $ver;
2568 doSNMPmining (getBypassValue(), $snmpsetup); // shows message by itself
2569 }
2570
2571 // File-related functions
2572 function addFileWithoutLink ()
2573 {
2574 setFuncMessages (__FUNCTION__, array ('OK' => 5, 'ERR1' => 207));
2575 assertStringArg ('comment', TRUE);
2576
2577 // Make sure the file can be uploaded
2578 if (get_cfg_var('file_uploads') != 1)
2579 throw new RackTablesError ('file uploads not allowed, change "file_uploads" parameter in php.ini', RackTablesError::MISCONFIGURED);
2580
2581 // Exit if the upload failed
2582 if ($_FILES['file']['error'])
2583 {
2584 showFuncMessage (__FUNCTION__, 'ERR1', array ($_FILES['file']['error']));
2585 return;
2586 }
2587 if (FALSE === $fp = fopen($_FILES['file']['tmp_name'], 'rb'))
2588 {
2589 showFuncMessage (__FUNCTION__, 'ERR1', array ('failed to access the temporary file'));
2590 return;
2591 }
2592
2593 $file_id = commitAddFile ($_FILES['file']['name'], $_FILES['file']['type'], $fp, genericAssertion ('comment', 'string0'));
2594 if (isset ($_REQUEST['taglist']))
2595 produceTagsForNewRecord ('file', $_REQUEST['taglist'], $file_id);
2596 showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
2597 }
2598
2599 function addFileToEntity ()
2600 {
2601 setFuncMessages (__FUNCTION__, array ('OK' => 5, 'ERR1' => 207));
2602 $realm = etypeByPageno();
2603 assertStringArg ('comment', TRUE);
2604
2605 // Make sure the file can be uploaded
2606 if (get_cfg_var('file_uploads') != 1)
2607 throw new RackTablesError ('file uploads not allowed, change "file_uploads" parameter in php.ini', RackTablesError::MISCONFIGURED);
2608
2609 // Exit if the upload failed
2610 if ($_FILES['file']['error'])
2611 {
2612 showFuncMessage (__FUNCTION__, 'ERR1', array ($_FILES['file']['error']));
2613 return;
2614 }
2615 if (FALSE === $fp = fopen($_FILES['file']['tmp_name'], 'rb'))
2616 {
2617 showFuncMessage (__FUNCTION__, 'ERR1', array ('failed to access the temporary file'));
2618 return;
2619 }
2620
2621 commitAddFile ($_FILES['file']['name'], $_FILES['file']['type'], $fp, genericAssertion ('comment', 'string0'));
2622 usePreparedInsertBlade
2623 (
2624 'FileLink',
2625 array
2626 (
2627 'file_id' => lastInsertID(),
2628 'entity_type' => $realm,
2629 'entity_id' => getBypassValue(),
2630 )
2631 );
2632 showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
2633 }
2634
2635 function linkFileToEntity ()
2636 {
2637 setFuncMessages (__FUNCTION__, array ('OK' => 71));
2638 $fi = spotEntity ('file', genericAssertion ('file_id', 'natural'));
2639 usePreparedInsertBlade
2640 (
2641 'FileLink',
2642 array
2643 (
2644 'file_id' => $fi['id'],
2645 'entity_type' => etypeByPageno(),
2646 'entity_id' => getBypassValue(),
2647 )
2648 );
2649 showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($fi['name'])));
2650 }
2651
2652 function replaceFile ()
2653 {
2654 setFuncMessages (__FUNCTION__, array ('OK' => 6, 'ERR2' => 201));
2655 // Make sure the file can be uploaded
2656 if (get_cfg_var('file_uploads') != 1)
2657 throw new RackTablesError ('file uploads not allowed, change "file_uploads" parameter in php.ini', RackTablesError::MISCONFIGURED);
2658 $shortInfo = spotEntity ('file', getBypassValue());
2659
2660 if (FALSE === $fp = fopen ($_FILES['file']['tmp_name'], 'rb'))
2661 {
2662 showFuncMessage (__FUNCTION__, 'ERR2');
2663 return;
2664 }
2665 commitReplaceFile ($shortInfo['id'], $fp);
2666
2667 showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
2668 }
2669
2670 function unlinkFile ()
2671 {
2672 setFuncMessages (__FUNCTION__, array ('OK' => 72));
2673 commitUnlinkFile (genericAssertion ('link_id', 'natural'));
2674 showFuncMessage (__FUNCTION__, 'OK');
2675 }
2676
2677 function deleteFile ()
2678 {
2679 setFuncMessages (__FUNCTION__, array ('OK' => 7));
2680 $file_id = genericAssertion ('file_id', 'natural');
2681 $shortInfo = spotEntity ('file', $file_id);
2682 commitDeleteFile ($file_id);
2683 showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
2684 }
2685
2686 function updateFileText ()
2687 {
2688 setFuncMessages (__FUNCTION__, array ('OK' => 6, 'ERR1' => 179, 'ERR2' => 155));
2689 $shortInfo = spotEntity ('file', getBypassValue());
2690 if ($shortInfo['mtime'] != genericAssertion ('mtime_copy', 'string'))
2691 {
2692 showFuncMessage (__FUNCTION__, 'ERR1');
2693 return;
2694 }
2695 commitReplaceFile ($shortInfo['id'], genericAssertion ('file_text', 'string0'));
2696 showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($shortInfo['name'])));
2697 }
2698
2699 function addIIFOIFCompatPack ()
2700 {
2701 setFuncMessages (__FUNCTION__, array ('OK' => 37));
2702 $standard = genericAssertion ('standard', 'enum/wdmstd');
2703 $iif_id = genericAssertion ('iif_id', 'iif');
2704 global $wdm_packs;
2705 $ngood = 0;
2706 foreach ($wdm_packs[$standard]['oif_ids'] as $oif_id)
2707 {
2708 commitSupplementPIC ($iif_id, $oif_id);
2709 $ngood++;
2710 }
2711 showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
2712 }
2713
2714 function addOIFCompat ()
2715 {
2716 $type1 = genericAssertion ('type1', 'natural');
2717 $type2 = genericAssertion ('type2', 'natural');
2718 $n_changed = addPortOIFCompat ($type1, $type2);
2719 showSuccess ("$n_changed row(s) added");
2720 }
2721
2722 function delOIFCompat ()
2723 {
2724 $type1 = genericAssertion ('type1', 'natural');
2725 $type2 = genericAssertion ('type2', 'natural');
2726 $n_changed = deletePortOIFCompat ($type1, $type2);
2727 showSuccess ("$n_changed row(s) deleted");
2728 }
2729
2730 function delIIFOIFCompatPack ()
2731 {
2732 setFuncMessages (__FUNCTION__, array ('OK' => 38));
2733 $standard = genericAssertion ('standard', 'enum/wdmstd');
2734 $iif_id = genericAssertion ('iif_id', 'iif');
2735 global $wdm_packs;
2736 $ngood = 0;
2737 foreach ($wdm_packs[$standard]['oif_ids'] as $oif_id)
2738 {
2739 usePreparedDeleteBlade ('PortInterfaceCompat', array ('iif_id' => $iif_id, 'oif_id' => $oif_id));
2740 $ngood++;
2741 }
2742 showFuncMessage (__FUNCTION__, 'OK', array ($ngood));
2743 }
2744
2745 function addOIFCompatPack ()
2746 {
2747 setFuncMessages (__FUNCTION__, array ('OK' => 21));
2748 global $wdm_packs;
2749 $oifs = $wdm_packs[genericAssertion ('standard', 'enum/wdmstd')]['oif_ids'];
2750 foreach ($oifs as $oif_id_1)
2751 {
2752 $args = $qmarks = array();
2753 $query = 'REPLACE INTO PortCompat (type1, type2) VALUES ';
2754 foreach ($oifs as $oif_id_2)
2755 {
2756 $qmarks[] = '(?, ?)';
2757 $args[] = $oif_id_1;
2758 $args[] = $oif_id_2;
2759 }
2760 $query .= implode (', ', $qmarks);
2761 usePreparedExecuteBlade ($query, $args);
2762 }
2763 showFuncMessage (__FUNCTION__, 'OK');
2764 }
2765
2766 function delOIFCompatPack ()
2767 {
2768 setFuncMessages (__FUNCTION__, array ('OK' => 21));
2769 global $wdm_packs;
2770 $oifs = $wdm_packs[genericAssertion ('standard', 'enum/wdmstd')]['oif_ids'];
2771 foreach ($oifs as $oif_id_1)
2772 foreach ($oifs as $oif_id_2)
2773 if ($oif_id_1 != $oif_id_2) # leave narrow-band mapping intact
2774 usePreparedDeleteBlade ('PortCompat', array ('type1' => $oif_id_1, 'type2' => $oif_id_2));
2775 showFuncMessage (__FUNCTION__, 'OK');
2776 }
2777
2778 function add8021QOrder ()
2779 {
2780 setFuncMessages (__FUNCTION__, array ('OK' => 48));
2781 $vdom_id = genericAssertion ('vdom_id', 'natural');
2782 $object_id = genericAssertion ('object_id', 'natural');
2783 $vst_id = genericAssertion ('vst_id', 'natural');
2784 global $pageno;
2785 fixContext();
2786 if ($pageno != 'object')
2787 spreadContext (spotEntity ('object', $object_id));
2788 if ($pageno != 'vst')
2789 spreadContext (spotEntity ('vst', $vst_id));
2790 assertPermission();
2791 usePreparedExecuteBlade
2792 (
2793 'INSERT INTO VLANSwitch (domain_id, object_id, template_id, last_change, out_of_sync) ' .
2794 'VALUES (?, ?, ?, NOW(), "yes")',
2795 array ($vdom_id, $object_id, $vst_id)
2796 );
2797 showFuncMessage (__FUNCTION__, 'OK');
2798 }
2799
2800 function del8021QOrder ()
2801 {
2802 setFuncMessages (__FUNCTION__, array ('OK' => 49));
2803 $object_id = genericAssertion ('object_id', 'natural');
2804 $vdom_id = genericAssertion ('vdom_id', 'natural');
2805 $vst_id = genericAssertion ('vst_id', 'natural');
2806 global $pageno;
2807 fixContext();
2808 if ($pageno != 'object')
2809 spreadContext (spotEntity ('object', $object_id));
2810 if ($pageno != 'vst')
2811 spreadContext (spotEntity ('vst', $vst_id));
2812 assertPermission();
2813 usePreparedDeleteBlade ('VLANSwitch', array ('object_id' => $object_id));
2814 $focus_hints = array
2815 (
2816 'prev_objid' => $object_id,
2817 'prev_vstid' => $vst_id,
2818 'prev_vdid' => $vdom_id,
2819 );
2820 showFuncMessage (__FUNCTION__, 'OK');
2821 return buildRedirectURL (NULL, NULL, $focus_hints);
2822 }
2823
2824 function createVLANDomain ()
2825 {
2826 setFuncMessages (__FUNCTION__, array ('OK' => 48));
2827 usePreparedInsertBlade
2828 (
2829 'VLANDomain',
2830 array
2831 (
2832 'description' => genericAssertion ('vdom_descr', 'string'),
2833 )
2834 );
2835 $domain_id = lastInsertID();
2836 lastCreated ('vdom', $domain_id);
2837 usePreparedInsertBlade
2838 (
2839 'VLANDescription',
2840 array
2841 (
2842 'domain_id' => $domain_id,
2843 'vlan_id' => VLAN_DFL_ID,
2844 'vlan_type' => 'compulsory',
2845 'vlan_descr' => 'default',
2846 )
2847 );
2848 showFuncMessage (__FUNCTION__, 'OK');
2849 }
2850
2851 function save8021QPorts ()
2852 {
2853 setFuncMessages (__FUNCTION__, array ('OK' => 21));
2854 global $sic;
2855 $object_id = getBypassValue();
2856 $form_mode = genericAssertion ('form_mode', 'string');
2857 $extra = array();
2858
2859 // prepare the $changes array
2860 $changes = array();
2861 switch ($form_mode)
2862 {
2863 case 'save':
2864 $nports = genericAssertion ('nports', 'natural');
2865 if ($nports == 1)
2866 $extra = array ('port_name' => genericAssertion ('pn_0', 'string'));
2867 for ($i = 0; $i < $nports; $i++)
2868 {
2869 $portname = assertStringArg ('pn_' . $i);
2870 $portmode = assertStringArg ('pm_' . $i);
2871 $native = isset ($sic['pnv_' . $i]) ? $sic['pnv_' . $i] : 0;
2872 switch ($portmode)
2873 {
2874 case 'trunk':
2875 # assertArrayArg ('pav_' . $i);
2876 if ($native != 0)
2877 genericAssertion ('pnv_' . $i, 'vlan1');
2878 $allowed = isset ($sic['pav_' . $i]) ? $sic['pav_' . $i] : array();
2879 break;
2880 case 'access':
2881 if ($native == 'same')
2882 continue 2;
2883 genericAssertion ('pnv_' . $i, 'vlan1');
2884 // An access port only generates a form input for its native VLAN,
2885 // the allowed VLAN list has to be derived.
2886 $allowed = array ($native);
2887 break;
2888 default:
2889 throw new InvalidRequestArgException ("pm_${i}", $portmode, 'unknown port mode');
2890 }
2891 $changes[$portname] = array
2892 (
2893 'mode' => $portmode,
2894 'allowed' => $allowed,
2895 'native' => $native,
2896 );
2897 }
2898 break;
2899 case 'duplicate':
2900 $from_port = genericAssertion ('from_port', 'string');
2901 $before = getStored8021QConfig ($object_id, 'desired');
2902 if (!array_key_exists ($from_port, $before))
2903 throw new InvalidArgException ('from_port', $from_port, 'this port does not exist');
2904 foreach (genericAssertion ('to_ports', 'array0') as $tpn)
2905 if (!array_key_exists ($tpn, $before))
2906 throw new InvalidArgException ('to_ports[]', $tpn, 'this port does not exist');
2907 elseif ($tpn != $from_port)
2908 $changes[$tpn] = $before[$from_port];
2909 break;
2910 default:
2911 throw new InvalidRequestArgException ('form_mode', $form_mode);
2912 }
2913 apply8021qChangeRequest ($object_id, $changes, TRUE, genericAssertion ('mutex_rev', 'unsigned'));
2914 return buildRedirectURL (NULL, NULL, $extra);
2915 }
2916
2917 function bindVLANtoIPv4 ()
2918 {
2919 setFuncMessages (__FUNCTION__, array ('OK' => 48));
2920 commitSupplementVLANIPv4 (genericAssertion ('vlan_ck', 'uint-vlan1'), genericAssertion ('id', 'natural'));
2921 showFuncMessage (__FUNCTION__, 'OK');
2922 }
2923
2924 function bindVLANtoIPv6 ()
2925 {
2926 setFuncMessages (__FUNCTION__, array ('OK' => 48));
2927 commitSupplementVLANIPv6 (genericAssertion ('vlan_ck', 'uint-vlan1'), genericAssertion ('id', 'natural'));
2928 showFuncMessage (__FUNCTION__, 'OK');
2929 }
2930
2931 function unbindVLANfromIPv4 ()
2932 {
2933 setFuncMessages (__FUNCTION__, array ('OK' => 49));
2934 commitReduceVLANIPv4 (genericAssertion ('vlan_ck', 'uint-vlan1'), genericAssertion ('id', 'natural'));
2935 showFuncMessage (__FUNCTION__, 'OK');
2936 }
2937
2938 function unbindVLANfromIPv6 ()
2939 {
2940 setFuncMessages (__FUNCTION__, array ('OK' => 49));
2941 commitReduceVLANIPv6 (genericAssertion ('vlan_ck', 'uint-vlan1'), genericAssertion ('id', 'natural'));
2942 showFuncMessage (__FUNCTION__, 'OK');
2943 }
2944
2945 function process8021QSyncRequest ()
2946 {
2947 setFuncMessages (__FUNCTION__, array ('OK' => 63, 'ERR' => 191));
2948 // behave depending on current operation: exec8021QPull or exec8021QPush
2949 global $op;
2950 if (FALSE === $done = exec8021QDeploy (getBypassValue(), $op == 'exec8021QPush'))
2951 showFuncMessage (__FUNCTION__, 'ERR');
2952 else
2953 showFuncMessage (__FUNCTION__, 'OK', array ($done));
2954 }
2955
2956 function process8021QRecalcRequest ()
2957 {
2958 setFuncMessages (__FUNCTION__, array ('OK' => 87));
2959 assertPermission (NULL, NULL, NULL, array (array ('tag' => '$op_recalc8021Q')));
2960 $counters = recalc8021QPorts (getBypassValue());
2961 if ($counters['ports'])
2962 showFuncMessage (__FUNCTION__, 'OK', array ($counters['ports'], $counters['switches']));
2963 else
2964 showNotice ('No changes were made');
2965 }
2966
2967 function resolve8021QConflicts ()
2968 {
2969 setFuncMessages (__FUNCTION__, array ('OK' => 63, 'ERR1' => 179, 'ERR2' => 109));
2970 global $sic, $dbxlink;
2971 $mutex_rev = genericAssertion ('mutex_rev', 'unsigned');
2972 $nrows = genericAssertion ('nrows', 'natural');
2973 $object_id = getBypassValue();
2974 // Divide submitted radio buttons into 3 groups:
2975 // left (saved version wins)
2976 // asis (ignore)
2977 // right (running version wins)
2978 $F = array();
2979 for ($i = 0; $i < $nrows; $i++)
2980 {
2981 if (!array_key_exists ("i_${i}", $sic))
2982 continue;
2983 // let's hope other inputs are in place
2984 switch ($sic["i_${i}"])
2985 {
2986 case 'left':
2987 case 'right':
2988 $F[$sic["pn_${i}"]] = array
2989 (
2990 'mode' => $sic["rm_${i}"],
2991 'allowed' => array_fetch ($sic, "ra_${i}", array()),
2992 'native' => $sic["rn_${i}"],
2993 'decision' => $sic["i_${i}"],
2994 );
2995 break;
2996 default:
2997 // don't care
2998 }
2999 }
3000 $dbxlink->beginTransaction();
3001 try
3002 {
3003 if (NULL === $vswitch = getVLANSwitchInfo ($object_id, 'FOR UPDATE'))
3004 throw new InvalidArgException ('object_id', $object_id, 'VLAN domain is not set for this object');
3005 if ($vswitch['mutex_rev'] != $mutex_rev)
3006 throw new InvalidRequestArgException ('mutex_rev', $mutex_rev, 'expired form (table data has changed)');
3007 $D = getStored8021QConfig ($vswitch['object_id'], 'desired');
3008 $C = getStored8021QConfig ($vswitch['object_id'], 'cached');
3009 $R = getRunning8021QConfig ($vswitch['object_id']);
3010 $plan = get8021QSyncOptions ($vswitch, $D, $C, $R['portdata']);
3011 $ndone = 0;
3012 foreach ($F as $port_name => $port)
3013 {
3014 if (!array_key_exists ($port_name, $plan))
3015 continue;
3016 elseif ($plan[$port_name]['status'] == 'merge_conflict')
3017 {
3018 // for R neither mutex nor revisions can be emulated, but revision change can be
3019 if (!same8021QConfigs ($port, $R['portdata'][$port_name]))
3020 throw new InvalidRequestArgException ("port ${port_name}", '(hidden)', 'expired form (switch data has changed)');
3021 if ($port['decision'] == 'right') // D wins, frame R by writing value of R to C
3022 $ndone += upd8021QPort ('cached', $vswitch['object_id'], $port_name, $port, $C[$port_name]);
3023 elseif ($port['decision'] == 'left') // R wins, cross D up
3024 $ndone += upd8021QPort ('cached', $vswitch['object_id'], $port_name, $D[$port_name], $C[$port_name]);
3025 // otherwise there was no decision made
3026 }
3027 elseif
3028 (
3029 $plan[$port_name]['status'] == 'delete_conflict' or
3030 $plan[$port_name]['status'] == 'martian_conflict'
3031 )
3032 if ($port['decision'] == 'left')
3033 // confirm deletion of local copy
3034 $ndone += del8021QPort ($vswitch['object_id'], $port_name);
3035 // otherwise ignore a decision that doesn't address a conflict
3036 }
3037 }
3038 catch (InvalidRequestArgException $e)
3039 {
3040 $dbxlink->rollBack();
3041 showFuncMessage (__FUNCTION__, 'ERR1');
3042 return;
3043 }
3044 catch (Exception $e)
3045 {
3046 $dbxlink->rollBack();
3047 showFuncMessage (__FUNCTION__, 'ERR2');
3048 return;
3049 }
3050 $dbxlink->commit();
3051 showFuncMessage (__FUNCTION__, 'OK', array ($ndone));
3052 }
3053
3054 function update8021QPortList()
3055 {
3056 genericAssertion ('ports', 'array');
3057 $enabled = $disabled = 0;
3058 $default_port = array
3059 (
3060 'mode' => 'access',
3061 'allowed' => array (VLAN_DFL_ID),
3062 'native' => VLAN_DFL_ID,
3063 );
3064 foreach (genericAssertion ('ports', 'array') as $line)
3065 if (preg_match ('/^enable (.+)$/', $line, $m))
3066 $enabled += add8021QPort (getBypassValue(), $m[1], $default_port);
3067 elseif (preg_match ('/^disable (.+)$/', $line, $m))
3068 $disabled += del8021QPort (getBypassValue(), $m[1]);
3069 else
3070 throw new InvalidRequestArgException ('ports[]', $line, 'malformed array item');
3071 # $enabled + $disabled > 0
3072 if ($enabled)
3073 showSuccess ("enabled 802.1Q for ${enabled} port(s)");
3074 if ($disabled)
3075 showSuccess ("disabled 802.1Q for ${disabled} port(s)");
3076 }
3077
3078 function cloneVST()
3079 {
3080 setFuncMessages (__FUNCTION__, array ('OK' => 48));
3081 $src_vst = spotEntity ('vst', genericAssertion ('from_id', 'natural'));
3082 amplifyCell ($src_vst);
3083 commitUpdateVSTRules (getBypassValue(), genericAssertion ('mutex_rev', 'unsigned'), $src_vst['rules']);
3084 showFuncMessage (__FUNCTION__, 'OK');
3085 }
3086
3087 function updVSTRule()
3088 {
3089 setFuncMessages (__FUNCTION__, array ('OK' => 43));
3090 // this is used for making throwing an invalid argument exception easier.
3091 function updVSTRule_get_named_param ($name, $haystack, &$last_used_name)
3092 {
3093 $last_used_name = $name;
3094 return isset ($haystack[$name]) ? $haystack[$name] : NULL;
3095 }
3096
3097 global $port_role_options;
3098 $vst_id = getBypassValue();
3099 $taglist = genericAssertion ('taglist', 'array0');
3100 $mutex_rev = genericAssertion ('mutex_rev', 'unsigned');
3101 $data = genericAssertion ('template_json', 'json');
3102 $rule_no = 0;
3103 try
3104 {
3105 $last_field = '';
3106 foreach ($data as $rule)
3107 {
3108 $rule_no++;
3109 if
3110 (
3111 ! isUnsignedInteger (updVSTRule_get_named_param ('rule_no', $rule, $last_field)) ||
3112 ! isPCRE (updVSTRule_get_named_param ('port_pcre', $rule, $last_field)) ||
3113 NULL === updVSTRule_get_named_param ('port_role', $rule, $last_field) ||
3114 ! array_key_exists (updVSTRule_get_named_param ('port_role', $rule, $last_field), $port_role_options) ||
3115 NULL === updVSTRule_get_named_param ('wrt_vlans', $rule, $last_field) ||
3116 ! preg_match ('/^[ 0-9\-,]*$/', updVSTRule_get_named_param ('wrt_vlans', $rule, $last_field)) ||
3117 NULL === updVSTRule_get_named_param ('description', $rule, $last_field)
3118 )
3119 throw new InvalidRequestArgException ($last_field, $rule[$last_field], "rule #$rule_no");
3120 }
3121 commitUpdateVSTRules ($vst_id, $mutex_rev, $data);
3122 }
3123 catch (Exception $e)
3124 {
3125 // Every case that is soft-processed in index.php?module=redirect will have
3126 // the working copy available for a retry.
3127 if ($e instanceof InvalidRequestArgException || $e instanceof RTDatabaseError)
3128 {
3129 startSession();
3130 $_SESSION['vst_edited'] = $data;
3131 session_commit();
3132 }
3133 throw $e;
3134 }
3135 rebuildTagChainForEntity ('vst', $vst_id, buildTagChainFromIds ($taglist), TRUE);
3136 showFuncMessage (__FUNCTION__, 'OK');
3137 }
3138