// All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. include_once '../db.inc.php'; include_once 'swreg.inc.php'; include_once '../swreg_creds.inc.php'; require_once '../functions.php'; require_once 'Log.php'; function process_macro($name, $value, $hn, $sn, $pid, &$logger) { $contents = ''; if (preg_match('/^file:(.*)/', $value, $match)) { if (!is_file($match[1])) { $logger->crit("Macro {$name} failed to evaluate: file {$match[1]} does not exist"); exit(1); } @$contents = file_get_contents(trim($match[1])); } elseif (preg_match('/^script:(.*)/', $value, $match)) { $script = trim($match[1]); $output = array(); $rc = 0; $hn = escapeshellarg($hn); $sn = escapeshellarg($sn); $pid = escapeshellarg($pid); @exec("$script $name $hn $sn $pid", $output, $rc); $contents = implode("\n", $output); if ($rc != 0) { $logger->crit("Macro {$name} failed to evaluate: {$contents}"); exit(1); } } else { $contents = $value; } return trim($contents); } $dsn = "$db_driver:host=$db_host;dbname=$db_name"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $dbh = new PDO($dsn, $db_user, $db_pass, $options); } catch (PDOException $e) { die($e->getMessage()); } $logger = Log::singleton('file', LOGFILE, TOOL_NAME . ' : Switch Registrar'); if ($logger === false) { die("Failed to open logfile.\n"); } $mask = Log::MAX(LOG_LEVEL); $logger->setMask($mask); $pid = $_GET['pid']; $sn = $_GET['sn']; $version = $_GET['version']; $num_ports = $_GET['num_ports']; $imagef = $_GET['imagef']; header('Content-type: text/plain'); if (!isset($pid) || !isset($sn) || !isset($version) || !isset($num_ports)) { echo "ERROR: Invalid request!\r\n"; $logger->emerg('Invalid request from ' . $_SERVER['REMOTE_ADDR'] . " pid = '$pid', sn = '$sn', version = '$version', num_ports = '$num_ports'"); exit(1); } $logger->debug("Received request from $sn: pid = $pid, version = $version, num_ports = $num_ports"); $image = null; $config = null; $found_pid = null; $not_configed = "ERROR: Tool not properly configured.\r\n"; if (isset($PID_ALIASES[$pid])) { $pid = $PID_ALIASES[$pid]; } foreach ($ZTP_PIDS as $zpid) { if (preg_match("/^$zpid$/", $pid)) { if (!isset($IMG_VERS[$zpid])) { echo $not_configed; $logger->emerg("\$IMG_VERS[$zpid] is not defined in swreg.inc.php!"); exit(1); } if (!isset($IMG_FILES[$zpid])) { echo $not_configed; $logger->emerg("\$IMG_FILES[$zpid] is not defined in swreg.inc.php!"); exit(1); } if ($version != $IMG_VERS[$zpid] || $imagef != $IMG_FILES[$zpid]) { $image = $IMG_FILES[$zpid]; } if (!isset($START_PORTS[$zpid])) { echo $not_configed; $logger->emerg("\$START_PORTS[$zpid] is not defined in swreg.inc.php!"); exit(1); } $start_port = $START_PORTS[$zpid]; if (!isset($PORT_TYPES[$zpid])) { echo $not_configed; $logger->emerg("\$PORT_TYPES[$zpid] is not defined in swreg.inc.php!"); exit(1); } $port_type = $PORT_TYPES[$zpid]; $found_pid = $zpid; } } if ($found_pid === null) { call_hook('REQUEST:ERROR', array($_SERVER['REMOTE_ADDR'], $sn, $pid, "Request from unknown PID: {$pid}")); $logger->crit("Request from unknown PID: $pid"); echo "ERROR: Unknown PID: $pid\r\n"; exit(1); } $use_pnp = false; if (USE_PNP === true) { foreach ($PNP_PATTERNS as $pattern => $ipnp) { if (preg_match("/$pattern/", $image)) { $use_pnp = $ipnp; break; } } } $sql = 'SELECT assigned_switch, max_ports, checked_out FROM DEVICE_MAP WHERE serial_number = ?'; $res = null; $sth = null; try { $sth = $dbh->prepare($sql); $res = $sth->execute(array($sn)); } catch (PDOException $e) { echo 'ERROR: Failed to get switch list: ' . $e->getMessage() . "\r\n"; call_hook('REQUEST:ERROR', array($_SERVER['REMOTE_ADDR'], $sn, $pid, 'Failed to get switch list: ' . $e->getMessage())); $logger->crit('Failed to get switch list: ' . $e->getMessage()); exit(1); } $row = $sth->fetch(); $sth->closeCursor(); $max_ports = $row['max_ports']; if ($row['checked_out'] == 0) { echo "ERROR: Switch has not yet been provisioned via the web\r\n"; $logger->crit("Switch $sn ({$_SERVER['REMOTE_ADDR']}) has not yet been provisioned via the web"); call_hook('REQUEST:UNKNOWN', array($_SERVER['REMOTE_ADDR'], $sn, $pid)); exit(1); } if ($max_ports > $num_ports) { echo "ERROR: Switch has been provisioned for $max_ports but this physical switch only has $num_ports\r\n"; call_hook('REQUEST:ERROR', array($_SERVER['REMOTE_ADDR'], $sn, $pid, "Switch has been provisioned for {$max_ports} but this physical switch only has {$num_ports}")); $logger->crit("Switch $sn has been provisioned for $max_ports but this physical switch only has $num_ports"); exit(1); } $sql = 'SELECT s.name AS hostname, s.location AS snmp_loc, s.is_idf AS is_idf, a.address AS ip, a.router AS gw, a.mask AS mask, a.mgmt_vlan AS mgmt_vlan, a.location AS location FROM SWITCHES s, ADDRESSES a WHERE s.name = ? AND s.ip_address = a.address'; try { $sth = $dbh->prepare($sql); $res = $sth->execute(array($row['assigned_switch'])); } catch (PDOException $e) { echo 'ERROR: Failed to get logical switch details: ' . $e->getMessage() . "\r\n"; call_hook('REQUEST:ERROR', array($_SERVER['REMOTE_ADDR'], $sn, $pid, 'Failed to get logical switch details: ' . $e->getMessage())); $logger->crit('Failed to get logical switch details: ' . $e->getMessage()); exit(1); } $row = $sth->fetch(); $sth->closeCursor(); call_hook('REQUEST:NEW', array($_SERVER['REMOTE_ADDR'], $row['hostname'], $sn, $pid, $version, $num_ports, $imagef)); if ($image !== null && !file_exists(TFTPBOOT . '/' . $image)) { echo "ERROR: Image file {$image} does not exist in " . TFTPBOOT . "\r\n"; call_hook('REQUEST:ERROR', array($_SERVER['REMOTE_ADDR'], $sn, $pid, "Image file {$image} does not exist in " . TFTPBOOT)); $logger->crit("Image file {$image} for {$sn}, {$pid} does not exist in " . TFTPBOOT); exit(1); } $sql = 'UPDATE DEVICE_MAP SET provisioned_status = ? WHERE serial_number = ?'; try { $sth = $dbh->prepare($sql); $sth->execute(array(PROVISION_IN_PROGRESS, $sn)); $sth->closeCursor(); } catch (PDOException $e) { call_hook('REQUEST:ERROR', array($_SERVER['REMOTE_ADDR'], $sn, $pid, "Failed to update switch provisioned status: {$e->getMessage()}")); echo "ERROR: Failed to update switch provisioned status: {$e->getMessage()}\r\n"; $logger->crit("Failed to update switch provisioned status: {$e->getMessage()}"); exit(1); } $port_contents = "interface range {$port_type}{$start_port} - $max_ports\n"; if (file_exists(PORT_TMPL_DIR . '/devices/' . $row['hostname'] . '-ports.tmpl')) { $port_contents = file_get_contents(PORT_TMPL_DIR . '/devices/' . $row['hostname'] . '-ports.tmpl'); if (file_exists(PORT_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-ports.tmpl')) { $port_contents .= file_get_contents(PORT_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-ports.tmpl'); } } else { if (file_exists(PORT_TMPL_DIR . '/ports.tmpl')) { $port_contents .= file_get_contents(PORT_TMPL_DIR . '/ports.tmpl'); } if (file_exists(PORT_TMPL_DIR . '/' . $found_pid . '/ports.tmpl')) { $port_contents .= file_get_contents(PORT_TMPL_DIR . '/' . $found_pid . '/ports.tmpl'); } if (file_exists(PORT_TMPL_DIR . '/DYNAMIC/ports.tmpl')) { $port_contents .= file_get_contents(PORT_TMPL_DIR . '/DYNAMIC/ports.tmpl'); } if (file_exists(PORT_TMPL_DIR . '/DYNAMIC/' . $found_pid . '/ports.tmpl')) { $port_contents .= file_get_contents(PORT_TMPL_DIR . '/DYNAMIC/' . $found_pid . '/ports.tmpl'); } if (file_exists(PORT_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-ports.tmpl')) { $port_contents .= file_get_contents(PORT_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-ports.tmpl'); } } $snmp_loc = $row['snmp_loc']; if (!$snmp_loc || $snmp_loc == '') { $snmp_loc = $row['location']; } $ew_keywords = preg_replace("/\s/", '_', $snmp_loc); $ew_role = 'ACCESS-SWITCH'; if ($row['is_idf'] == 1) { $ew_role = 'IDF-SWITCH'; } $contents = ''; if (file_exists(DEVICE_TMPL_DIR . '/device.tmpl')) { $contents .= file_get_contents(DEVICE_TMPL_DIR . '/device.tmpl'); } if (file_exists(DEVICE_TMPL_DIR . '/' . $found_pid . '/device.tmpl')) { $contents .= file_get_contents(DEVICE_TMPL_DIR . '/' . $found_pid . '/device.tmpl'); } if (file_exists(DEVICE_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-device.tmpl')) { $contents .= file_get_contents(DEVICE_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-device.tmpl'); } $contents .= "end\n"; $eem_contents = ''; if (file_exists(EEM_TMPL_DIR . '/eem.tmpl')) { $eem_contents = file_get_contents(EEM_TMPL_DIR . '/eem.tmpl'); } if (file_exists(EEM_TMPL_DIR . '/' . $found_pid . '/eem.tmpl')) { $eem_contents .= file_get_contents(EEM_TMPL_DIR . '/' . $found_pid . '/eem.tmpl'); } if (file_exists(EEM_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-eem.tmpl')) { $eem_contents .= file_get_contents(EEM_TMPL_DIR . '/device-overrides/' . $row['hostname'] . '-eem.tmpl'); } $contents = str_replace('%%EEM_CONFIG%%', $eem_contents, $contents); $vlan_contents = file_get_contents(VLAN_TMPL_DIR . '/' . strtolower($row['location']) . '-vlan.tmpl'); $contents = str_replace('%%VLAN_TMPL%%', $vlan_contents, $contents); $contents = str_replace('%%PORT_CONFIG%%', $port_contents, $contents); $contents = str_replace('%%MGMT_VLAN%%', $row['mgmt_vlan'], $contents); $contents = str_replace('%%HOSTNAME%%', $row['hostname'], $contents); $contents = str_replace('%%EW_DOMAIN%%', $row['location'], $contents); $contents = str_replace('%%EW_KEYWORDS%%', $ew_keywords, $contents); $contents = str_replace('%%EW_ROLE%%', $ew_role, $contents); $contents = str_replace('%%VTP_DOMAIN%%', $row['location'], $contents); $contents = str_replace('%%MGMT_IP%%', $row['ip'], $contents); $contents = str_replace('%%MGMT_MASK%%', $row['mask'], $contents); $contents = str_replace('%%MGMT_GW%%', $row['gw'], $contents); $contents = str_replace('%%SNMP_LOCATION%%', $snmp_loc, $contents); if (MAP_V6 === true) { $octets = explode('.', $row['ip']); $contents = str_replace('%%MGMT_IP6%%', IPV6_PREFIX . ':' . $octets[3], $contents); $contents = str_replace('%%MGMT_PREFIXLEN%%', IPV6_PREFIXLEN, $contents); $contents = str_replace('%%MGMT_GW6%%', IPV6_GW, $contents); } $contents = str_replace('%%START_PORT%%', $start_port, $contents); $contents = str_replace('%%MAX_PORT%%', $max_ports, $contents); // XXX: TODO $contents = str_replace('%%STACK_CONFIG%%', '!', $contents); // Replace creds $contents = str_replace('%%ADMIN_SECRET%%', ADMIN_SECRET, $contents); $contents = str_replace('%%TACACS_KEY%%', TACACS_KEY, $contents); $contents = str_replace('%%EW_SHARED_SECRET%%', EW_SHARED_SECRET, $contents); $contents = str_replace('%%EW_MGMT_SHARED_SECRET%%', EW_MGMT_SHARED_SECRET, $contents); $contents = str_replace('%%SNMPV3_USER%%', SNMPV3_USER, $contents); $contents = str_replace('%%SNMPV3_PASS%%', SNMPV3_PASS, $contents); if (isset($VLAN_OVERRIDES) && isset($VLAN_OVERRIDES[$row['location']])) { foreach ($VLAN_OVERRIDES[$row['location']] as $vn => $vi) { $VLANS[$vn] = $vi; } } foreach ($VLANS as $vn => $vi) { $vname = strtoupper($vn); $vname = str_replace(' ', '_', $vname); $macro = '%%' . $vname . '_VLAN%%'; $contents = str_replace($macro, $vi, $contents); } $custom_macros = array(); if (isset($CUSTOM_MACROS)) { foreach ($CUSTOM_MACROS as $macro => $mv) { $custom_macros[$macro] = process_macro($macro, $mv, $row['hostname'], $sn, $found_pid, $logger); } } if (isset($MDF_OVERRIDES) && isset($MDF_OVERRIDES[$row['location']])) { foreach ($MDF_OVERRIDES[$row['locations']] as $macro => $mv) { $custom_macros[$macro] = process_macro($macro, $mv, $row['hostname'], $sn, $found_pid, $logger); } } if (isset($PID_MACROS) && isset($PID_MACROS[$found_pid])) { foreach ($PID_MACROS[$found_pid] as $macro => $mv) { $custom_macros[$macro] = process_macro($macro, $mv, $row['hostname'], $sn, $found_pid, $logger); } } foreach ($custom_macros as $macro => $mv) { $contents = str_replace("%%$macro%%", $mv, $contents); } umask(0022); $fd = @fopen(DEVICE_CONFIG_DIR . '/' . $row['hostname'] . '-config.txt', 'w'); fwrite($fd, $contents); fclose($fd); $fd = @fopen(DEVICE_TMP_DIR . '/' . $row['hostname'] . '-config.txt', 'w'); fclose($fd); chmod(DEVICE_TMP_DIR . '/' . $row['hostname'] . '-config.txt', 0666); if ($use_pnp) { $error = ''; $ticket = apicGetTicket(APIC_HOST, APIC_USER, APIC_PASS, $error); if ($ticket === false) { $logger->error("Failed to get ticket from APIC-EM: $error"); $use_pnp = false; } else { $proj = apicGetProject(APIC_HOST, $ticket, APIC_PROJECT, $error); if ($proj === false) { $logger->error("Failed to get project from APIC-EM: $error"); $use_pnp = false; } else { $ares = apicAddDevice(APIC_HOST, $ticket, $proj['id'], $row['hostname'], $sn, $pid, $error, $config, $image); if ($ares === false) { $logger->error("Failed to add device to APIC-EM: $error"); $use_pnp = false; } } } } if ($use_pnp) { echo "PNP\r\n"; $logger->info("Successfully provisioned switch $sn ($pid) {$row['hostname']} as a PnP device"); } else { echo "Config: {$row['hostname']}-config.txt\r\n"; if (isset($SDM_PROFILES[$pid])) { echo "SDM: {$SDM_PROFILES[$pid]}\r\n"; } if ($image !== null) { echo "Image: $image\r\n"; } $logger->info("Successfully provisioned switch $sn ($pid) {$row['hostname']}"); } $logger->close();