Merge branch 'master' of https://github.com/RackTables/racktables.git
[racktables] / scripts / syncdomain.php
1 #!/usr/bin/env php
2 <?php
3
4 # This file is a part of RackTables, a datacenter and server room management
5 # framework. See accompanying file "COPYING" for the full copyright and
6 # licensing information.
7
8 $script_mode = TRUE;
9 require 'inc/init.php';
10
11 function usage()
12 {
13 echo "Usage: <this file> <options>\n";
14 echo "\t\t--vdid=<VLAN domain ID>\n";
15 echo "\t\t--mode=pull\n";
16 echo "\t\t--mode=pullall\n";
17 echo "\t\t--mode=push\n";
18 echo "\t\t[--max=<max_to_do>]\n";
19 echo "\t\t[--verbose]\n";
20 exit (1);
21 }
22
23 function print_message_line($text)
24 {
25 echo gmdate (DATE_RFC1123) . ": ${text}\n";
26 }
27
28 $options = getopt ('', array ('vdid:', 'max::', 'mode:', 'verbose'));
29 if (!array_key_exists ('mode', $options))
30 usage();
31
32 switch ($options['mode'])
33 {
34 case 'pullall':
35 $do_push = FALSE;
36 break;
37 case 'pull':
38 $do_push = FALSE;
39 break;
40 case 'push':
41 $do_push = TRUE;
42 break;
43 default:
44 usage();
45 }
46
47 $max = array_key_exists ('max', $options) ? $options['max'] : 0;
48 $verbose = array_key_exists ('verbose', $options);
49
50 $switch_list = array();
51 if (! isset ($options['vdid']))
52 $switch_list = getVLANSwitches();
53 else
54 try
55 {
56 $mydomain = getVLANDomain ($options['vdid']);
57 foreach ($mydomain['switchlist'] as $switch)
58 $switch_list[] = $switch['object_id'];
59 }
60 catch (RackTablesError $e)
61 {
62 print_message_line ("Cannot load domain data with ID ${options['vdid']}");
63 print_message_line ($e->getMessage());
64 exit (1);
65 }
66
67 $todo = array
68 (
69 'pull' => array ('sync_ready', 'resync_ready'),
70 'push' => array ('sync_ready', 'resync_ready'),
71 'pullall' => array ('sync_ready', 'resync_ready', 'sync_aging', 'resync_aging', 'done'),
72 );
73
74 $domain_key = isset ($options['vdid']) ? $options['vdid'] : 0;
75 $filename = '/var/tmp/RackTables-syncdomain-' . $domain_key . '.pid';
76 if (FALSE === $fp = @fopen ($filename, 'x+'))
77 {
78 if (FALSE === $pidfile_mtime = filemtime ($filename))
79 {
80 print_message_line ("Failed to obtain mtime of ${filename}");
81 exit (1);
82 }
83 $current_time = time();
84 if ($current_time < $pidfile_mtime)
85 {
86 print_message_line ("Warning: pidfile ${filename} mtime is in future!");
87 exit (1);
88 }
89 // don't indicate failure unless the pidfile is 15 minutes or more old
90 if ($current_time < $pidfile_mtime + 15 * 60)
91 exit (0);
92 print_message_line ("Failed to lock ${filename}, already locked by PID " . mb_substr (file_get_contents ($filename), 0, 6));
93 exit (1);
94 }
95
96 ftruncate ($fp, 0);
97 fwrite ($fp, getmypid() . "\n");
98 fclose ($fp);
99
100 // fetch all the needed data from DB (preparing for DB connection loss)
101 $switch_queue = array();
102 foreach ($switch_list as $object_id)
103 if (in_array (detectVLANSwitchQueue (getVLANSwitchInfo ($object_id)), $todo[$options['mode']]))
104 {
105 $cell = spotEntity ('object', $object_id);
106 if (considerConfiguredConstraint ($cell, 'SYNC_802Q_LISTSRC'))
107 $switch_queue[] = $cell;
108 }
109
110 // YOU SHOULD NOT USE DB FUNCTIONS BELOW IN THE PARENT PROCESS
111 // THE PARENT'S DB CONNECTION IS LOST DUE TO RECONNECTING IN THE CHILD
112 $fork_slots = getConfigVar ('SYNCDOMAIN_MAX_PROCESSES');
113 $do_fork = ($fork_slots > 1) and extension_loaded ('pcntl');
114 if ($fork_slots > 1 and ! $do_fork)
115 throw new RackTablesError ('PHP extension \'pcntl\' not found, can not use childs', RackTablesError::MISCONFIGURED);
116 $switches_working = 0;
117 $switchesdone = 0;
118 foreach ($switch_queue as $object)
119 {
120 if ($do_fork)
121 {
122 // wait for the next free slot
123 while ($fork_slots <= $switches_working)
124 {
125 pcntl_waitpid (-1, $wait_status);
126 --$switches_working;
127 }
128 $i_am_child = (0 === $fork_res = pcntl_fork());
129 }
130 if (! $do_fork or $i_am_child)
131 {
132 try
133 {
134 // make a separate DB connection for correct concurrent transactions handling
135 if ($i_am_child)
136 connectDB();
137 $portsdone = exec8021QDeploy ($object['id'], $do_push);
138 if ($portsdone or $verbose)
139 print_message_line ("Done '${object['dname']}': ${portsdone}");
140 }
141 catch (RackTablesError $e)
142 {
143 print_message_line ("FAILED '${object['dname']}': " . $e->getMessage());
144 }
145 if ($i_am_child)
146 exit (0);
147 }
148 if (isset ($fork_res) and $fork_res > 0)
149 ++$switches_working;
150
151 if (++$switchesdone == $max)
152 {
153 if ($verbose)
154 print_message_line ("Maximum of ${max} items reached, terminating");
155 break;
156 }
157 }
158
159 // wait for all childs to exit
160 while ($switches_working > 0)
161 {
162 --$switches_working;
163 pcntl_waitpid (-1, $wait_status);
164 }
165
166 if (FALSE === unlink ($filename))
167 {
168 print_message_line ("Failed removing pidfile ${filename}");
169 exit (1);
170 }
171 exit (0);
172 ?>