r5073 renderRackSpaceForObject(): add LABEL for Zero-U checkbox
[racktables] / wwwroot / inc / remote.php
CommitLineData
7cb57a03
AA
1<?php
2
cddbb9fd
DO
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
7cb57a03
AA
7function queryDevice ($object_id, $command)
8{
9 $breed = detectDeviceBreed ($object_id);
10 if (empty ($breed))
11 throw new RTGatewayError ("Can not determine device breed");
12
13 if (! validBreedFunction ($breed, $command))
14 throw new RTGatewayError ("unsupported command '$command' for the breed '$breed'");
15
16 require_once 'deviceconfig.php';
17 global $breedfunc;
18 if (! is_callable ($breedfunc["$breed-$command-main"]))
19 throw new RTGatewayError ("undeclared function '" . $breedfunc["$breed-$command-main"] . "'");
20 $query = translateDeviceCommands ($object_id, array (array ('opcode' => $command)));
21 if ($command == 'xlatepushq')
22 return $query;
23 else
24 {
25 $answer = queryTerminal ($object_id, $query, FALSE);
26 return $breedfunc["$breed-$command-main"] ($answer);
27 }
28}
29
30function translateDeviceCommands ($object_id, $crq, $vlan_names = NULL)
31{
32 require_once 'deviceconfig.php';
33 $breed = detectDeviceBreed ($object_id);
34 if (empty ($breed))
35 throw new RTGatewayError ("Can not determine device breed");
36
37 if (! validBreedFunction ($breed, 'xlatepushq'))
38 throw new RTGatewayError ("unsupported command 'xlatepushq' for the breed '$breed'");
39
40 global $breedfunc;
41 return $breedfunc["$breed-xlatepushq-main"] ($object_id, $crq, $vlan_names);
42}
43
44// This function returns a text output received from the device
45// You can override connection settings by implement a callback named 'terminal_settings'.
46// Errors are thrown as exceptions if not $tolerate_remote_errors, and shown as warnings otherwise.
47function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)
48{
49 $objectInfo = spotEntity ('object', $object_id);
50 $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
51 if (count ($endpoints) == 0)
52 throw new RTGatewayError ('no management address set');
53 if (count ($endpoints) > 1)
54 throw new RTGatewayError ('cannot pick management address');
55
5d2988fb 56 // telnet prompt and mode specification
7cb57a03
AA
57 switch ($breed = detectDeviceBreed ($object_id))
58 {
77b7e03c
DO
59 case 'ios12':
60 case 'fdry5':
a3447df9
AA
61 case 'ftos8':
62 $protocol = 'netcat'; // default is netcat mode
349394c1 63 $prompt = '^(Login|Username|Password): $|^\S+[>#]$'; // set the prompt in case user would like to specify telnet protocol
7cb57a03 64 break;
d4a957e0
DO
65 case 'air12':
66 $protocol = 'telnet'; # Aironet IOS is broken
67 $prompt = '^(Username|Password): $|^\S+[>#]$';
68 break;
7cb57a03
AA
69 case 'vrp53':
70 case 'vrp55':
5d2988fb 71 $protocol = 'telnet';
338465a8 72 $prompt = '^\[[^[\]]+\]$|^<[^<>]+>$|^(Username|Password):$|(?:\[Y\/N\]|\(Y\/N\)\[[YN]\]):?$';
7cb57a03
AA
73 break;
74 case 'nxos4':
5d2988fb 75 $protocol = 'telnet';
7cb57a03
AA
76 $prompt = '[>:#] $';
77 break;
78 case 'xos12':
5d2988fb 79 $protocol = 'telnet';
18997a72 80 $prompt = ': $|\.\d+ # $|\?\s*\([Yy]\/[Nn]\)\s*$';
7cb57a03
AA
81 break;
82 case 'jun10':
5d2988fb 83 $protocol = 'telnet';
7cb57a03
AA
84 $prompt = '^login: $|^Password:$|^\S+@\S+[>#] $';
85 break;
85703049
DO
86 case 'eos4':
87 $protocol = 'telnet'; # strict RFC854 implementation, netcat won't work
88 $prompt = '^(\xf2?login|Username|Password): $|^\S+[>#]$';
89 break;
5d2988fb
AA
90 default:
91 $protocol = 'netcat';
92 $prompt = NULL;
7cb57a03
AA
93 }
94
95 // set the default settings before calling user-defined callback
96 $settings = array
97 (
98 'hostname' => $endpoints[0],
a3447df9 99 'protocol' => $protocol,
7cb57a03
AA
100 'port' => NULL,
101 'prompt' => $prompt,
102 'username' => NULL,
103 'password' => NULL,
ada8a703 104 'timeout' => 15,
7cb57a03 105 'connect_timeout' => 2,
a9edde6c 106 'prompt_delay' => 0.001, # 1ms
7cb57a03
AA
107 'sudo_user' => NULL,
108 'identity_file' => NULL,
109 );
110 if (is_callable ('terminal_settings'))
111 call_user_func ('terminal_settings', $objectInfo, array (&$settings)); // override settings
112
01b0c68f
AA
113 if (! isset ($settings['port']) and $settings['protocol'] == 'netcat')
114 $settings['port'] = 23;
115
7cb57a03
AA
116 $params = array ( $settings['hostname'] );
117 $params_from_settings = array();
118 switch ($settings['protocol'])
119 {
120 case 'telnet':
01b0c68f
AA
121 case 'netcat':
122 // prepend command list with vendor-specific disabling pager command
7cb57a03
AA
123 switch ($breed)
124 {
125 case 'ios12':
126 $commands = "terminal length 0\n" . $commands;
127 break;
128 case 'nxos4':
52bf6129 129 case 'air12':
0a2d7ff1 130 case 'ftos8':
7cb57a03
AA
131 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
132 break;
133 case 'xos12':
134 $commands = "disable clipaging\n" . $commands;
135 break;
136 case 'vrp55':
137 $commands = "screen-length 0 temporary\n" . $commands;
138 break;
139 case 'fdry5':
140 $commands = "skip-page-display\n" . $commands;
141 break;
142 case 'jun10':
184ec25d 143 $commands = "set cli screen-length 0\n" . $commands;
7cb57a03 144 break;
85703049
DO
145 case 'eos4':
146 $commands = "enable\nno terminal monitor\nterminal length 0\n" . $commands;
147 break;
7cb57a03
AA
148 }
149 // prepend telnet commands by credentials
150 if (isset ($settings['password']))
151 $commands = $settings['password'] . "\n" . $commands;
152 if (isset ($settings['username']))
153 $commands = $settings['username'] . "\n" . $commands;
01b0c68f
AA
154 // command-line options are specific to client: telnet or netcat
155 switch ($settings['protocol'])
156 {
157 case 'telnet':
158 $params_from_settings['port'] = 'port';
159 $params_from_settings['prompt'] = 'prompt';
160 $params_from_settings['connect-timeout'] = 'connect_timeout';
161 $params_from_settings['timeout'] = 'timeout';
162 $params_from_settings['prompt-delay'] = 'prompt_delay';
163 break;
164 case 'netcat':
56930ac4 165 $params_from_settings['p'] = 'port';
01b0c68f 166 $params_from_settings['w'] = 'timeout';
56930ac4 167 $params_from_settings['b'] = 'ncbin';
01b0c68f
AA
168 break;
169 }
7cb57a03
AA
170 break;
171 case 'ssh':
172 $params_from_settings['port'] = 'port';
424604b4 173 $params_from_settings['proto'] = 'proto';
7cb57a03
AA
174 $params_from_settings['username'] = 'username';
175 $params_from_settings['i'] = 'identity_file';
176 $params_from_settings['sudo-user'] = 'sudo_user';
177 $params_from_settings['connect-timeout'] = 'connect_timeout';
178 break;
179 default:
c0b84dfb 180 throw RTGatewayError ("Invalid terminal protocol '${settings['protocol']}' specified");
7cb57a03
AA
181 }
182 foreach ($params_from_settings as $param_name => $setting_name)
183 if (isset ($settings[$setting_name]))
01b0c68f
AA
184 if (is_int ($param_name))
185 $params[] = $settings[$setting_name];
186 else
187 $params[$param_name] = $settings[$setting_name];
7cb57a03
AA
188
189 $ret_code = callScript ($settings['protocol'], $params, $commands, $out, $errors);
01b0c68f 190 if ($settings['protocol'] != 'ssh' || ! $tolerate_remote_errors)
7cb57a03
AA
191 {
192 if (! empty ($errors))
e75026f8 193 throw new RTGatewayError ("${settings['protocol']} error: " . rtrim ($errors));
7cb57a03
AA
194 elseif ($ret_code !== 0)
195 throw new RTGatewayError ("${settings['protocol']} error: result code $ret_code");
196 }
197 elseif (! empty ($errors)) // ssh and not tolerate and non-empty $errors
198 foreach (explode ("\n", $errors) as $line)
199 if (strlen ($line))
200 showWarning ("${settings['protocol']} ${settings['hostname']}: $line");
201 return strtr($out, array("\r" => "")); // cut ^M symbols
202}
203
204function callScript ($gwname, $params, $in, &$out, &$errors)
205{
206 global $racktables_gwdir, $local_gwdir;
207 if (isset ($local_gwdir) && file_exists ("$local_gwdir/$gwname"))
208 $dir = $local_gwdir;
209 elseif (isset ($racktables_gwdir) && file_exists ("$racktables_gwdir/$gwname"))
210 $dir = $racktables_gwdir;
211 if (! isset ($dir))
c0b84dfb 212 throw new RTGatewayError ("Could not find the gateway file called '$gwname'");
7cb57a03
AA
213
214 $cmd_line = "./$gwname";
215 foreach ($params as $key => $value)
216 {
217 if (! isset ($value))
218 continue;
219 if (preg_match ('/^\d+$/', $key))
220 $cmd_line .= " " . escapeshellarg ($value);
221 else
222 {
223 if (strlen ($key) == 1)
224 $cmd_line .= " " . escapeshellarg ("-$key") . " " . escapeshellarg ($value);
225 else
226 $cmd_line .= " " . escapeshellarg("--$key=$value");
227 }
228 }
424604b4 229
7cb57a03
AA
230 $pipes = array();
231 $child = proc_open
232 (
233 $cmd_line,
234 array (
235 0 => array ('pipe', 'r'),
236 1 => array ('pipe', 'w'),
237 2 => array ('pipe', 'w'),
238 ),
239 $pipes,
240 $dir
241 );
242 if (! is_resource ($child))
c0b84dfb 243 throw new RTGatewayError ("cant execute $dir/$gwname");
7cb57a03
AA
244 fwrite ($pipes[0], $in);
245 fclose ($pipes[0]);
246 $out = stream_get_contents ($pipes[1]);
247 $errors = stream_get_contents ($pipes[2]);
248 return proc_close ($child);
249}
250
bc93236a
AA
251function getRunning8021QConfig ($object_id)
252{
253 $ret = queryDevice ($object_id, 'get8021q');
254 // Once there is no default VLAN in the parsed data, it means
255 // something else was parsed instead of config text.
1c0a61e2 256 if (!in_array (VLAN_DFL_ID, $ret['vlanlist']) || empty ($ret['portdata']))
bc93236a
AA
257 throw new RTGatewayError ('communication with device failed');
258 return $ret;
259}
260
261function setDevice8021QConfig ($object_id, $pseudocode, $vlan_names)
262{
263 // FIXME: this is a perfect place to log intended changes
264 // $object_id argument isn't used by default translating functions, but
265 // may come in handy for overloaded versions of these.
9f1da7de 266 $commands = translateDeviceCommands ($object_id, $pseudocode, $vlan_names);
dc68fd36
AA
267 $breed = detectDeviceBreed ($object_id);
268 $output = queryTerminal ($object_id, $commands, FALSE);
269
270 // throw an exception if Juniper did not allow to enter config mode or to commit changes
271 if ($breed == 'jun10')
272 {
273 if (preg_match ('/>\s*configure exclusive\s*$[^#>]*?^error:/sm', $output))
274 throw new RTGatewayError ("Configuration is locked by other user");
275 elseif (preg_match ('/#\s*commit\s*$([^#]*?^error: .*?)$/sm', $output, $m))
276 throw new RTGatewayError ("Commit failed: ${m[1]}");
277 }
bc93236a
AA
278}
279
7cb57a03 280?>