예제 #1
0
function renderEntitySummary($cell, $title, $values = array())
{
    global $page_by_realm;
    // allow plugins to override summary table
    $values = callHook('modifyEntitySummary', $cell, $values);
    startPortlet($title);
    echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
    foreach ($values as $name => $value) {
        if (is_array($value) and count($value) == 1) {
            $value = array_shift($value);
            echo $value;
            continue;
        }
        if (is_array($value)) {
            $name = array_shift($value);
            $value = array_shift($value);
        } elseif (!is_array($value)) {
            $name .= ':';
        }
        $class = 'tdright';
        $m = array();
        if (preg_match('/^\\{(.*?)\\}(.*)/', $name, $m)) {
            $class .= ' ' . $m[1];
            $name = $m[2];
        }
        if ($name == 'tags:') {
            $baseurl = '';
            if (isset($page_by_realm[$cell['realm']])) {
                $baseurl = makeHref(array('page' => $page_by_realm[$cell['realm']], 'tab' => 'default')) . "&";
            }
            printTagTRs($cell, $baseurl);
        } else {
            echo "<tr><th width='50%' class='{$class}'>{$name}</th><td class=tdleft>{$value}</td></tr>";
        }
    }
    echo "</table>\n";
    finishPortlet();
}
예제 #2
0
function generateSLBConfig2($triplet_list)
{
    $ret = '';
    global $parser_class;
    $gl_parser = new $parser_class();
    $defaults = getSLBDefaults(TRUE);
    $gl_parser->addMacro('GLOBAL_VS_CONF', dos2unix($defaults['vsconfig']));
    $gl_parser->addMacro('GLOBAL_RS_CONF', dos2unix($defaults['rsconfig']));
    $gl_parser->addMacro('RSPORT', '%VPORT%');
    $gl_parser->addMacro('VS_PREPEND', "# LB (id == %LB_ID%): %LB_NAME%\n# VSG (id == %VSG_ID%): %VS_NAME%\n# RS (id == %RSP_ID%): %RSP_NAME%");
    // group triplets by object_id, vs_id
    $grouped = array();
    foreach ($triplet_list as $triplet) {
        $grouped[$triplet['object_id']][$triplet['vs_id']][] = $triplet;
    }
    foreach ($grouped as $object_id => $subarr) {
        $seen_vs_groups = array();
        $lb_parser = clone $gl_parser;
        $lb_cell = spotEntity('object', $object_id);
        $lb_parser->addMacro('LB_ID', $lb_cell['id']);
        $lb_parser->addMacro('LB_NAME', $lb_cell['name']);
        foreach ($subarr as $vs_id => $triplets) {
            $vs_parser = clone $lb_parser;
            $vs_cell = spotEntity('ipvs', $vs_id);
            if (!isset($vs_cell['ports']) || !isset($vs_cell['vips'])) {
                amplifyCell($vs_cell);
            }
            $vs_parser->addMacro('VS_ID', $vs_cell['id']);
            $vs_parser->addMacro('VSG_ID', $vs_cell['id']);
            $vs_parser->addMacro('VS_NAME', $vs_cell['name']);
            $vs_parser->addMacro('VS_RS_CONF', dos2unix($vs_cell['rsconfig']));
            foreach ($triplets as $triplet) {
                $virtual_services = array();
                $tr_parser = clone $vs_parser;
                $rs_cell = spotEntity('ipv4rspool', $triplet['rspool_id']);
                $tr_parser->addMacro('RSP_ID', $rs_cell['id']);
                $tr_parser->addMacro('RSP_NAME', $rs_cell['name']);
                $tr_parser->addMacro('RSP_VS_CONF', dos2unix($rs_cell['vsconfig']));
                $tr_parser->addMacro('RSP_RS_CONF', dos2unix($rs_cell['rsconfig']));
                $tr_parser->addMacro('VS_VS_CONF', dos2unix($vs_cell['vsconfig']));
                // VS-driven vsconfig has higher priority than RSP-driven
                foreach ($triplet['ports'] as $port_row) {
                    $is_mark = $port_row['proto'] == 'MARK';
                    $p_parser = clone $tr_parser;
                    $p_parser->addMacro('VS_HEADER', $is_mark ? 'fwmark %MARK%' : '%VIP% %VPORT%');
                    $p_parser->addMacro('PROTO', $is_mark ? 'TCP' : $port_row['proto']);
                    $p_parser->addMacro($is_mark ? 'MARK' : 'VPORT', $port_row['vport']);
                    foreach ($vs_cell['ports'] as $vport) {
                        if ($vport['vport'] == $port_row['vport'] && $vport['proto'] == $port_row['proto']) {
                            $p_parser->addMacro('PORT_VS_CONF', dos2unix($vport['vsconfig']));
                            $p_parser->addMacro('PORT_RS_CONF', dos2unix($vport['rsconfig']));
                            break;
                        }
                    }
                    $p_parser->addMacro('SLB_PORT_VS_CONF', dos2unix($port_row['vsconfig']));
                    $p_parser->addMacro('SLB_PORT_RS_CONF', dos2unix($port_row['rsconfig']));
                    if ($is_mark) {
                        $p_parser->addMacro('RS_HEADER', '%RSIP%');
                        // find enabled IP families to fill IP_VER
                        $seen_families = array();
                        foreach ($triplet['vips'] as $ip_row) {
                            $family_length = strlen($ip_row['vip']);
                            $seen_families[$family_length] = $family_length == 16 ? 6 : 4;
                        }
                        if (!$seen_families) {
                            $seen_families['unknown'] = '';
                        }
                        foreach ($seen_families as $ip_ver) {
                            $fam_parser = clone $p_parser;
                            if ($ip_ver) {
                                $fam_parser->addMacro('IP_VER', $ip_ver);
                            }
                            if ('' != ($vs_config = generateVSSection($fam_parser))) {
                                $virtual_services["IPv{$ip_ver} " . $fam_parser->expandMacro('VS_HEADER')] = $vs_config;
                            }
                        }
                    } else {
                        $p_parser->addMacro('RS_HEADER', '%RSIP% %RSPORT%');
                        foreach ($triplet['vips'] as $ip_row) {
                            $ip_parser = clone $p_parser;
                            $ip_parser->addMacro('VIP', ip_format($ip_row['vip']));
                            $ip_parser->addMacro('IP_VER', strlen($ip_row['vip']) == 16 ? 6 : 4);
                            $ip_parser->addMacro('PRIO', $ip_row['prio']);
                            foreach ($vs_cell['vips'] as $vip) {
                                if ($vip['vip'] === $ip_row['vip']) {
                                    $ip_parser->addMacro('VIP_VS_CONF', dos2unix($vip['vsconfig']));
                                    $ip_parser->addMacro('VIP_RS_CONF', dos2unix($vip['rsconfig']));
                                    break;
                                }
                            }
                            $ip_parser->addMacro('SLB_VIP_VS_CONF', dos2unix($ip_row['vsconfig']));
                            $ip_parser->addMacro('SLB_VIP_RS_CONF', dos2unix($ip_row['rsconfig']));
                            if ('' != ($vs_config = generateVSSection($ip_parser))) {
                                $virtual_services[$port_row['proto'] . " " . $ip_parser->expandMacro('VS_HEADER')] = $vs_config;
                            }
                        }
                        // vips
                    }
                }
                //ports
                // group multiple virtual_services into vs_groups
                $groups = array();
                foreach ($virtual_services as $key => $content) {
                    $groups[$content][] = preg_replace('/^(TCP|UDP|IPv[46]?)\\s+/', '', $key);
                }
                foreach ($groups as $content => $keys) {
                    if (NULL !== ($new_content = callHook('generateSLBConfig_stage2', $content, $keys))) {
                        $content = $new_content;
                    }
                    $ret .= $tr_parser->expand("\n%VS_PREPEND%\n");
                    if (count($keys) == 1) {
                        $ret .= "virtual_server " . array_first($keys) . " {\n" . $content . "}\n";
                    } else {
                        // come up with the name for new VS group
                        $vsg_name = makeUniqueVSGName($seen_vs_groups, $keys, $vs_cell);
                        $seen_vs_groups[$vsg_name] = 1;
                        $tr_parser->addMacro('VSG_NAME', $vsg_name);
                        $ret .= $tr_parser->expand("virtual_server_group %VSG_NAME% {\n");
                        foreach ($keys as $vs_header) {
                            $ret .= "\t{$vs_header}\n";
                        }
                        $ret .= "}\n";
                        $ret .= $tr_parser->expand("virtual_server group %VSG_NAME% {\n");
                        $ret .= $content . "}\n";
                    }
                }
            }
            // triplets
        }
        // vs
    }
    // balancers
    return $ret;
}
예제 #3
0
파일: ophandlers.php 프로젝트: xtha/salt
function doVSMigrate()
{
    global $dbxlink;
    $vs_id = assertUIntArg('vs_id');
    $vs_cell = spotEntity('ipvs', $vs_id);
    amplifyCell($vs_cell);
    $tag_ids = genericAssertion('taglist', 'array0');
    $old_vs_list = genericAssertion('vs_list', 'array');
    $plan = callHook('buildVSMigratePlan', $vs_id, $old_vs_list);
    $dbxlink->beginTransaction();
    // remove all triplets
    usePreparedDeleteBlade('VSEnabledIPs', array('vs_id' => $vs_id));
    usePreparedDeleteBlade('VSEnabledPorts', array('vs_id' => $vs_id));
    // remove all VIPs and ports that are in $plan and create new ones
    foreach ($plan['vips'] as $vip) {
        usePreparedDeleteBlade('VSIPs', array('vs_id' => $vs_id, 'vip' => $vip['vip']));
        usePreparedInsertBlade('VSIPs', array('vs_id' => $vs_id) + $vip);
    }
    foreach ($plan['ports'] as $port) {
        usePreparedDeleteBlade('VSPorts', array('vs_id' => $vs_id, 'proto' => $port['proto'], 'vport' => $port['vport']));
        usePreparedInsertBlade('VSPorts', array('vs_id' => $vs_id) + $port);
    }
    // create triplets
    foreach ($plan['triplets'] as $triplet) {
        $tr_key = array('vs_id' => $triplet['vs_id'], 'object_id' => $triplet['object_id'], 'rspool_id' => $triplet['rspool_id']);
        foreach ($triplet['ports'] as $port) {
            addSLBPortLink($tr_key + $port);
        }
        foreach ($triplet['vips'] as $vip) {
            addSLBIPLink($tr_key + $vip);
        }
    }
    // update configs
    usePreparedUpdateBlade('VS', $plan['properties'], array('id' => $vs_id));
    // replace tags
    global $taglist;
    $chain = array();
    foreach ($tag_ids as $tid) {
        if (!isset($taglist[$tid])) {
            $dbxlink->rollback();
            showError("Unknown tag id {$tid}");
        } else {
            $chain[] = $taglist[$tid];
        }
    }
    rebuildTagChainForEntity('ipvs', $vs_id, $chain, TRUE);
    $dbxlink->commit();
    showSuccess("old VS configs were copied to VS group");
    return buildRedirectURL(NULL, 'default');
}
예제 #4
0
    $model = rtrim($controller, 's');
    $controller .= 'Controller';
    $dispatch = new $controller($model, $controllerName, $action);
    if ((int) method_exists($controller, $action)) {
        call_user_func_array(array($dispatch, $action), $queryString);
    } else {
        /* Error Generation Code Here */
    }
}
/** Autoload any classes that are required **/
function __autoload($className)
{
    if (file_exists(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php')) {
        require_once ROOT . DS . 'library' . DS . strtolower($className) . '.class.php';
    } else {
        if (file_exists(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php')) {
            require_once ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php';
        } else {
            if (file_exists(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php')) {
                require_once ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php';
            } else {
                /* Error Generation Code Here */
            }
        }
    }
}
setReporting();
removeMagicQuotes();
unregisterGlobals();
callHook();
예제 #5
0
function getIPv6AddressNetworkId($ip_bin, $masklen = 128)
{
    if ($row = callHook('fetchIPv6AddressNetworkRow', $ip_bin, $masklen)) {
        return $row['id'];
    }
    return NULL;
}
예제 #6
0
    $admin_account = spotEntity('user', 1);
    if (isCLIMode() && FALSE !== ($env_user = getenv('USER'))) {
        // use USER env var if we are in CLI mode
        $remote_username = $env_user;
    } else {
        $remote_username = $admin_account['user_name'];
    }
    unset($env_user);
    unset($admin_account);
}
$virtual_obj_types = explode(',', getConfigVar('VIRTUAL_OBJ_LISTSRC'));
alterConfigWithUserPreferences();
$op = '';
// load additional plugins
ob_start();
foreach (glob("{$racktables_plugins_dir}/*.php") as $filename) {
    require_once $filename;
}
// display plugins output if it contains something but newlines
$tmp = ob_get_clean();
if ($tmp != '' and !preg_match("/^\n+\$/D", $tmp)) {
    echo $tmp;
}
unset($tmp);
// These will be filled in by fixContext()
$expl_tags = array();
$impl_tags = array();
// Initial chain for the current target.
$target_given_tags = array();
callHook('initFinished');
function portletRSPoolAddMany($pool_id)
{
    startPortlet('Add many');
    printOpFormIntro('addMany');
    echo "<table border=0 align=center>\n<tr><td>";
    if (getConfigVar('DEFAULT_IPV4_RS_INSERVICE') == 'yes') {
        printImageHREF('inservice', 'in service');
    } else {
        printImageHREF('notinservice', 'NOT in service');
    }
    echo "</td><td>Format: ";
    $formats = callHook('getBulkRealsFormats');
    printSelect($formats, array('name' => 'format'));
    echo "</td><td><input type=submit value=Parse></td></tr>\n";
    echo "<tr><td colspan=3><textarea name=rawtext cols=100 rows=25></textarea></td></tr>\n";
    echo "</table>\n";
    finishPortlet();
}
예제 #8
0
function buildLVSConfig($object_id)
{
    return callHook('buildEntityLVSConfig', spotEntity('object', $object_id));
}
예제 #9
0
파일: interface.php 프로젝트: xtha/salt
function renderObject8021QSyncSchedule($object, $vswitch, $maxdecisions)
{
    echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
    // FIXME: sort rows newest event last
    $rows = array();
    if (!considerConfiguredConstraint($object, 'SYNC_802Q_LISTSRC')) {
        $rows['auto sync'] = '<span class="trerror">disabled by operator</span>';
    }
    $rows['last local change'] = datetimestrFromTimestamp($vswitch['last_change']) . ' (' . formatAge($vswitch['last_change']) . ')';
    $rows['device out of sync'] = $vswitch['out_of_sync'];
    if ($vswitch['out_of_sync'] == 'no') {
        $push_duration = $vswitch['last_push_finished'] - $vswitch['last_push_started'];
        $rows['last sync session with device'] = datetimestrFromTimestamp($vswitch['last_push_started']) . ' (' . formatAge($vswitch['last_push_started']) . ', ' . ($push_duration < 0 ? 'interrupted' : "lasted {$push_duration}s") . ')';
    }
    if ($vswitch['last_errno']) {
        $rows['failed'] = datetimestrFromTimestamp($vswitch['last_error_ts']) . ' (' . strerror8021Q($vswitch['last_errno']) . ')';
    }
    if (NULL !== ($new_rows = callHook('alter8021qSyncSummaryItems', $rows))) {
        $rows = $new_rows;
    }
    foreach ($rows as $th => $td) {
        echo "<tr><th width='50%' class=tdright>{$th}:</th><td class=tdleft colspan=2>{$td}</td></tr>";
    }
    echo '<tr><th class=tdright>run now:</th><td class=tdcenter>';
    printOpFormIntro('exec8021QPull');
    echo getImageHREF('prev', 'pull remote changes in', TRUE, 101) . '</form></td><td class=tdcenter>';
    if ($maxdecisions) {
        echo getImageHREF('COMMIT gray', 'cannot push due to version conflict(s)');
    } else {
        printOpFormIntro('exec8021QPush');
        echo getImageHREF('COMMIT', 'push local changes out', TRUE, 102) . '</form>';
    }
    echo '</td></tr>';
    echo '</table>';
}
function renderIPVSConvert($vs_id)
{
    $old_vs_list = callHook('getVSIDsByGroup', $vs_id);
    $grouped = array();
    $used_tags = array();
    foreach ($old_vs_list as $old_vs_id) {
        $vsinfo = spotEntity('ipv4vs', $old_vs_id);
        foreach ($vsinfo['etags'] as $taginfo) {
            $used_tags[$taginfo['id']] = $taginfo;
        }
        $port_key = $vsinfo['proto'] . '-' . $vsinfo['vport'];
        $grouped[$port_key][] = $vsinfo;
    }
    startPortlet("Found " . count($old_vs_list) . " matching VS");
    printOpFormIntro('convert');
    if (count($used_tags)) {
        echo '<p>Assign these tags to VS group:</p>';
        foreach ($used_tags as $taginfo) {
            echo '<p><label><input type=checkbox checked name="taglist[]" value="' . htmlspecialchars($taginfo['id'], ENT_QUOTES) . '""> ' . serializeTags(array($taginfo)) . '<label>';
        }
    }
    echo '<p>Import settings of these VS:</p>';
    echo '<table align=center><tr>';
    foreach ($grouped as $port_key => $list) {
        echo '<th>' . $port_key . '</th>';
    }
    echo '</tr><tr>';
    foreach ($grouped as $port_key => $list) {
        echo '<td><table>';
        foreach ($list as $vsinfo) {
            echo '<tr><td><input type=checkbox name="vs_list[]" checked value="' . htmlspecialchars($vsinfo['id'], ENT_QUOTES) . '"></td><td>';
            renderSLBEntityCell($vsinfo);
            echo '</td></tr>';
        }
        echo '</table></td>';
    }
    echo '</tr></table>';
    printImageHREF('next', "Import settings of the selected services", TRUE);
    echo '</form>';
    finishPortlet();
}
예제 #11
0
function renderObject8021QSync($object_id)
{
    $vswitch = getVLANSwitchInfo($object_id);
    $object = spotEntity('object', $object_id);
    try {
        $R = getRunning8021QConfig($object_id);
    } catch (Exception $re) {
        showWarning('Device configuration unavailable:<br>' . $re->getMessage());
        return;
    }
    $D = getStored8021QConfig($vswitch['object_id'], 'desired');
    $C = getStored8021QConfig($vswitch['object_id'], 'cached');
    $plan = apply8021QOrder($vswitch['template_id'], get8021QSyncOptions($vswitch, $D, $C, $R['portdata']));
    $maxdecisions = 0;
    foreach ($plan as $port) {
        if ($port['status'] == 'delete_conflict' or $port['status'] == 'merge_conflict' or $port['status'] == 'add_conflict' or $port['status'] == 'martian_conflict') {
            $maxdecisions++;
        }
    }
    if (isset($_REQUEST['hl_port_id'])) {
        assertUIntArg('hl_port_id');
        $hl_port_id = intval($_REQUEST['hl_port_id']);
        $hl_port_name = NULL;
        addAutoScrollScript("port-{$hl_port_id}");
        amplifyCell($object);
        foreach ($object['ports'] as $port) {
            if (mb_strlen($port['name']) && $port['id'] == $hl_port_id) {
                $hl_port_name = $port['name'];
                break;
            }
        }
    }
    echo '<table border=0 class=objectview cellspacing=0 cellpadding=0>';
    echo '<tr><td class=pcleft width="50%">';
    startPortlet('schedule');
    echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
    // FIXME: sort rows newest event last
    $rows = array();
    if (!considerConfiguredConstraint($object, 'SYNC_802Q_LISTSRC')) {
        $rows['auto sync'] = '<span class="trerror">disabled by operator</span>';
    }
    $rows['last local change'] = $vswitch['last_change'] . ' (' . $vswitch['last_change_age'] . ' ago)';
    $rows['device out of sync'] = $vswitch['out_of_sync'];
    if ($vswitch['out_of_sync'] == 'no') {
        $rows['last sync session with device'] = $vswitch['last_push_finished'] . ' (' . $vswitch['last_push_age'] . ' ago, lasted ' . $vswitch['last_push_lasted'] . ')';
    }
    if ($vswitch['last_errno']) {
        $rows['failed'] = $vswitch['last_error_ts'] . ' (' . strerror8021Q($vswitch['last_errno']) . ')';
    }
    if (NULL !== ($new_rows = callHook('alter8021qSyncSummaryItems', $rows))) {
        $rows = $new_rows;
    }
    foreach ($rows as $th => $td) {
        echo "<tr><th width='50%' class=tdright>{$th}:</th><td class=tdleft colspan=2>{$td}</td></tr>";
    }
    echo '<tr><th class=tdright>run now:</th><td class=tdcenter>';
    printOpFormIntro('exec8021QPull');
    echo getImageHREF('prev', 'pull remote changes in', TRUE, 101) . '</form></td><td class=tdcenter>';
    if ($maxdecisions) {
        echo getImageHREF('COMMIT gray', 'cannot push due to version conflict(s)');
    } else {
        printOpFormIntro('exec8021QPush');
        echo getImageHREF('COMMIT', 'push local changes out', TRUE, 102) . '</form>';
    }
    echo '</td></tr>';
    echo '</table>';
    finishPortlet();
    startPortlet('preview legend');
    echo '<table cellspacing=0 cellpadding=5 align=center class=widetable>';
    echo '<tr><th>status</th><th width="50%">color code</th></tr>';
    echo '<tr><td class=tdright>with template role:</td><td class=trbusy>&nbsp;</td></tr>';
    echo '<tr><td class=tdright>without template role:</td><td>&nbsp;</td></tr>';
    echo '<tr><td class=tdright>new data:</td><td class=trok>&nbsp;</td></tr>';
    echo '<tr><td class=tdright>warnings in new data:</td><td class=trwarning>&nbsp;</td></tr>';
    echo '<tr><td class=tdright>fatal errors in new data:</td><td class=trerror>&nbsp;</td></tr>';
    echo '<tr><td class=tdright>deleted data:</td><td class=trnull>&nbsp;</td></tr>';
    echo '</table>';
    finishPortlet();
    echo '</td><td class=pcright>';
    startPortlet('preview/resolve');
    switchportInfoJS($object_id);
    // load JS code to make portnames interactive
    // initialize one of three popups: we've got data already
    $port_config = addslashes(json_encode(formatPortConfigHints($object_id, $R)));
    addJS(<<<END
\$(document).ready(function(){
\tvar confData = \$.parseJSON('{$port_config}');
\tapplyConfData(confData);
\tvar menuItem = \$('.context-menu-item.itemname-conf');
\tmenuItem.addClass(\$.contextMenu.disabledItemClassName);
\tsetItemIcon(menuItem[0], 'ok');
});
END
, TRUE);
    echo '<table cellspacing=0 cellpadding=5 align=center class=widetable width="100%">';
    if ($maxdecisions) {
        echo '<tr><th colspan=2>&nbsp;</th><th colspan=3>discard</th><th>&nbsp;</th></tr>';
    }
    echo '<tr valign=top><th>port</th><th width="40%">last&nbsp;saved&nbsp;version</th>';
    if ($maxdecisions) {
        addJS('js/racktables.js');
        printOpFormIntro('resolve8021QConflicts', array('mutex_rev' => $vswitch['mutex_rev']));
        foreach (array('left', 'asis', 'right') as $pos) {
            echo "<th class=tdcenter><input type=radio name=column_radio value={$pos} " . "onclick=\"checkColumnOfRadios('i_', {$maxdecisions}, '_{$pos}')\"></th>";
        }
    }
    echo '<th width="40%">running&nbsp;version</th></tr>';
    $rownum = 0;
    $plan = sortPortList($plan);
    $domvlans = array_keys(getDomainVLANs($vswitch['domain_id']));
    $default_port = array('mode' => 'access', 'allowed' => array(VLAN_DFL_ID), 'native' => VLAN_DFL_ID);
    foreach ($plan as $port_name => $item) {
        $trclass = $left_extra = $right_extra = $left_text = $right_text = '';
        $radio_attrs = array();
        switch ($item['status']) {
            case 'ok_to_delete':
                $left_text = serializeVLANPack($item['left']);
                $right_text = 'none';
                $left_extra = ' trnull';
                $right_extra = ' trok';
                // no confirmation is necessary
                break;
            case 'delete_conflict':
                $trclass = 'trbusy';
                $left_extra = ' trerror';
                // can be fixed on request
                $right_extra = ' trnull';
                $left_text = formatVLANPackDiff($item['lastseen'], $item['left']);
                $right_text = '&nbsp;';
                $radio_attrs = array('left' => '', 'asis' => ' checked', 'right' => ' disabled');
                // dummy setting to suppress warnings in resolve8021QConflicts()
                $item['right'] = $default_port;
                break;
            case 'add_conflict':
                $trclass = 'trbusy';
                $right_extra = ' trerror';
                $left_text = '&nbsp;';
                $right_text = serializeVLANPack($item['right']);
                break;
            case 'ok_to_add':
                $trclass = 'trbusy';
                $right_extra = ' trok';
                $left_text = '&nbsp;';
                $right_text = serializeVLANPack($item['right']);
                break;
            case 'ok_to_merge':
                $trclass = 'trbusy';
                $left_extra = ' trok';
                $right_extra = ' trok';
                // fall through
            // fall through
            case 'in_sync':
                $trclass = 'trbusy';
                $left_text = $right_text = serializeVLANPack($item['both']);
                break;
            case 'ok_to_pull':
                // at least one of the sides is not in the default state
                $trclass = 'trbusy';
                $right_extra = ' trok';
                $left_text = serializeVLANPack($item['left']);
                $right_text = serializeVLANPack($item['right']);
                break;
            case 'ok_to_push':
                $trclass = ' trbusy';
                $left_extra = ' trok';
                $left_text = formatVLANPackDiff($C[$port_name], $item['left']);
                $right_text = serializeVLANPack($item['right']);
                break;
            case 'merge_conflict':
                $trclass = 'trbusy';
                $left_extra = ' trerror';
                $right_extra = ' trerror';
                $left_text = formatVLANPackDiff($C[$port_name], $item['left']);
                $right_text = serializeVLANPack($item['right']);
                // enable, but consider each option independently
                // Don't accept running VLANs not in domain, and
                // don't offer anything, that VST will deny.
                // Consider domain and template constraints.
                $radio_attrs = array('left' => '', 'asis' => ' checked', 'right' => '');
                if (!acceptable8021QConfig($item['right']) or count(array_diff($item['right']['allowed'], $domvlans)) or !goodModeForVSTRole($item['right']['mode'], $item['vst_role'])) {
                    $radio_attrs['left'] = ' disabled';
                }
                break;
            case 'ok_to_push_with_merge':
                $trclass = 'trbusy';
                $left_extra = ' trok';
                $right_extra = ' trwarning';
                $left_text = formatVLANPackDiff($C[$port_name], $item['left']);
                $right_text = serializeVLANPack($item['right']);
                break;
            case 'none':
                $left_text = '&nbsp;';
                $right_text = '&nbsp;';
                break;
            case 'martian_conflict':
                if ($item['right']['mode'] == 'none') {
                    $right_text = '&nbsp;';
                } else {
                    $right_text = serializeVLANPack($item['right']);
                    $right_extra = ' trerror';
                }
                if ($item['left']['mode'] == 'none') {
                    $left_text = '&nbsp;';
                } else {
                    $left_text = serializeVLANPack($item['left']);
                    $left_extra = ' trerror';
                    $radio_attrs = array('left' => '', 'asis' => ' checked', 'right' => ' disabled');
                    // idem, see above
                    $item['right'] = $default_port;
                }
                break;
            default:
                $trclass = 'trerror';
                $left_text = $right_text = 'internal rendering error';
                break;
        }
        $ancor = '';
        $td_class = '';
        if (isset($hl_port_name) and $hl_port_name == $port_name) {
            $ancor = "name='port-{$hl_port_id}'";
            $td_class = ' border_highlight';
        }
        echo "<tr class='{$trclass}'><td class='tdleft{$td_class}' NOWRAP><a class='interactive-portname port-menu nolink' {$ancor}>{$port_name}</a></td>";
        if (!count($radio_attrs)) {
            echo "<td class='tdleft{$left_extra}'>{$left_text}</td>";
            if ($maxdecisions) {
                echo '<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>';
            }
            echo "<td class='tdleft{$right_extra}'>{$right_text}</td>";
        } else {
            echo "<td class='tdleft{$left_extra}'><label for=i_{$rownum}_left>{$left_text}</label></td>";
            foreach ($radio_attrs as $pos => $attrs) {
                echo "<td><input id=i_{$rownum}_{$pos} name=i_{$rownum} type=radio value={$pos}{$attrs}></td>";
            }
            echo "<td class='tdleft{$right_extra}'><label for=i_{$rownum}_right>{$right_text}</label></td>";
        }
        echo '</tr>';
        if (count($radio_attrs)) {
            echo "<input type=hidden name=rm_{$rownum} value=" . $item['right']['mode'] . '>';
            echo "<input type=hidden name=rn_{$rownum} value=" . $item['right']['native'] . '>';
            foreach ($item['right']['allowed'] as $a) {
                echo "<input type=hidden name=ra_{$rownum}[] value={$a}>";
            }
            echo "<input type=hidden name=pn_{$rownum} value='" . htmlspecialchars($port_name) . "'>";
        }
        $rownum += count($radio_attrs) ? 1 : 0;
    }
    if ($rownum) {
        echo "<input type=hidden name=nrows value={$rownum}>";
        echo '<tr><td colspan=2>&nbsp;</td><td colspan=3 align=center class=tdcenter>';
        printImageHREF('UNLOCK', 'resolve conflicts', TRUE);
        echo '</td><td>&nbsp;</td></tr>';
    }
    echo '</table>';
    echo '</form>';
    finishPortlet();
    echo '</td></tr></table>';
}
예제 #12
0
function queryTerminal($object_id, $commands, $tolerate_remote_errors = TRUE)
{
    $objectInfo = spotEntity('object', $object_id);
    $endpoints = findAllEndpoints($object_id, $objectInfo['name']);
    if (count($endpoints) == 0) {
        throw new RTGatewayError('no management address set');
    }
    if (count($endpoints) > 1) {
        throw new RTGatewayError('cannot pick management address');
    }
    // telnet prompt and mode specification
    switch ($breed = detectDeviceBreed($object_id)) {
        case 'ios12':
        case 'fdry5':
        case 'ftos8':
            $protocol = 'netcat';
            // default is netcat mode
            $prompt = '^(Login|Username|Password): $|^\\S+[>#]$';
            // set the prompt in case user would like to specify telnet protocol
            break;
        case 'air12':
            $protocol = 'telnet';
            # Aironet IOS is broken
            $prompt = '^(Username|Password): $|^\\S+[>#]$';
            break;
        case 'vrp53':
        case 'vrp55':
            $protocol = 'telnet';
            $prompt = '^\\[[^[\\]]+\\]$|^<[^<>]+>$|^(Username|Password):$|(?:\\[Y\\/N\\]|\\(Y\\/N\\)\\[[YN]\\]):?$';
            break;
        case 'nxos4':
            $protocol = 'telnet';
            $prompt = '(^([Ll]ogin|[Pp]assword):|[>#]) $';
            break;
        case 'xos12':
            $protocol = 'telnet';
            $prompt = ': $|\\.\\d+ # $|\\?\\s*\\([Yy]\\/[Nn]\\)\\s*$';
            break;
        case 'jun10':
            $protocol = 'telnet';
            $prompt = '^login: $|^Password:$|^\\S+@\\S+[>#] $';
            break;
        case 'eos4':
            $protocol = 'telnet';
            # strict RFC854 implementation, netcat won't work
            $prompt = '^(\\xf2?login|Username|Password): $|^\\S+[>#]$';
            break;
        case 'ros11':
            $protocol = 'netcat';
            # see ftos8 case
            $prompt = '^(User Name|\\rPassword):$|^\\r?\\S+# $';
            break;
        case 'iosxr4':
            $protocol = 'telnet';
            $prompt = '^\\r?(Login|Username|Password): $|^\\r?\\S+[>#]$';
            break;
        case 'ucs':
            $protocol = 'ucssdk';
            break;
        default:
            $protocol = 'netcat';
            $prompt = NULL;
    }
    // set the default settings before calling user-defined callback
    $settings = array('hostname' => $endpoints[0], 'protocol' => $protocol, 'port' => NULL, 'prompt' => $prompt, 'username' => NULL, 'password' => NULL, 'timeout' => 15, 'connect_timeout' => 2, 'prompt_delay' => 0.001, 'sudo_user' => NULL, 'identity_file' => NULL);
    if (is_callable('terminal_settings')) {
        call_user_func('terminal_settings', $objectInfo, array(&$settings));
    }
    // override settings
    if (!isset($settings['port']) and $settings['protocol'] == 'netcat') {
        $settings['port'] = 23;
    }
    $params = array($settings['hostname']);
    $params_from_settings = array();
    switch ($settings['protocol']) {
        case 'telnet':
        case 'netcat':
            // prepend command list with vendor-specific disabling pager command
            switch ($breed) {
                case 'ios12':
                    $commands = "terminal length 0\n" . $commands;
                    break;
                case 'nxos4':
                case 'air12':
                case 'ftos8':
                    $commands = "terminal length 0\nterminal no monitor\n" . $commands;
                    break;
                case 'xos12':
                    $commands = "disable clipaging\n" . $commands;
                    break;
                case 'vrp55':
                    $commands = "screen-length 0 temporary\n" . $commands;
                    break;
                case 'fdry5':
                    $commands = "skip-page-display\n" . $commands;
                    break;
                case 'jun10':
                    $commands = "set cli screen-length 0\n" . $commands;
                    break;
                case 'eos4':
                    $commands = "enable\nno terminal monitor\nterminal length 0\n" . $commands;
                    break;
                case 'ros11':
                    $commands = "terminal datadump\n" . $commands;
                    $commands .= "\n\n";
                    # temporary workaround for telnet server
                    break;
                case 'iosxr4':
                    $commands = "terminal length 0\nterminal monitor disable\n" . $commands;
                    break;
                case 'dlink':
                    $commands = "disable clipaging\n" . $commands;
                    break;
            }
            // prepend telnet commands by credentials
            if (isset($settings['password'])) {
                $commands = $settings['password'] . "\n" . $commands;
            }
            if (isset($settings['username'])) {
                $commands = $settings['username'] . "\n" . $commands;
            }
            // command-line options are specific to client: telnet or netcat
            switch ($settings['protocol']) {
                case 'telnet':
                    $params_from_settings['port'] = 'port';
                    $params_from_settings['prompt'] = 'prompt';
                    $params_from_settings['connect-timeout'] = 'connect_timeout';
                    $params_from_settings['timeout'] = 'timeout';
                    $params_from_settings['prompt-delay'] = 'prompt_delay';
                    break;
                case 'netcat':
                    $params_from_settings['p'] = 'port';
                    $params_from_settings['w'] = 'timeout';
                    $params_from_settings['b'] = 'ncbin';
                    break;
            }
            break;
        case 'ssh':
            $params_from_settings['port'] = 'port';
            $params_from_settings['proto'] = 'proto';
            $params_from_settings['username'] = '******';
            $params_from_settings['i'] = 'identity_file';
            $params_from_settings['sudo-user'] = '******';
            $params_from_settings['connect-timeout'] = 'connect_timeout';
            break;
        case 'ucssdk':
            # remote XML through a Python backend
            $params = array();
            # reset
            # UCS in its current implementation besides the terminal_settings() provides
            # an additional username/password feed through the HTML from. Whenever the
            # user provides the credentials through the form, use these instead of the
            # credentials [supposedly] set by terminal_settings().
            if ($script_mode != TRUE && !isCheckSet('use_terminal_settings')) {
                genericAssertion('ucs_login', 'string');
                genericAssertion('ucs_password', 'string');
                $settings['username'] = $_REQUEST['ucs_login'];
                $settings['password'] = $_REQUEST['ucs_password'];
            }
            foreach (array('hostname', 'username', 'password') as $item) {
                if (empty($settings[$item])) {
                    throw new RTGatewayError("{$item} not available, check terminal_settings()");
                }
            }
            $commands = "login {$settings['hostname']} {$settings['username']} {$settings['password']}\n" . $commands;
            break;
        default:
            throw RTGatewayError("Invalid terminal protocol '{$settings['protocol']}' specified");
    }
    foreach ($params_from_settings as $param_name => $setting_name) {
        if (isset($settings[$setting_name])) {
            if (is_int($param_name)) {
                $params[] = $settings[$setting_name];
            } else {
                $params[$param_name] = $settings[$setting_name];
            }
        }
    }
    callHook('alterTerminalParams', $object_id, $tolerate_remote_errors, array(&$settings['protocol']), array(&$params));
    $ret_code = callScript($settings['protocol'], $params, $commands, $out, $errors);
    if ($settings['protocol'] != 'ssh' || !$tolerate_remote_errors) {
        if (!empty($errors)) {
            throw new RTGatewayError("{$settings['protocol']} error: " . rtrim($errors));
        } elseif ($ret_code !== 0) {
            throw new RTGatewayError("{$settings['protocol']} error: result code {$ret_code}");
        }
    } elseif (!empty($errors)) {
        // ssh and tolerate and non-empty $errors
        foreach (explode("\n", $errors) as $line) {
            if (strlen($line)) {
                showWarning("{$settings['protocol']} {$settings['hostname']}: {$line}");
            }
        }
    }
    return strtr($out, array("\r" => ""));
    // cut ^M symbols
}
예제 #13
0
function queryTerminal($object_id, $commands, $tolerate_remote_errors = TRUE)
{
    $objectInfo = spotEntity('object', $object_id);
    $endpoints = findAllEndpoints($object_id, $objectInfo['name']);
    if (count($endpoints) == 0) {
        throw new RTGatewayError('no management address set');
    }
    if (count($endpoints) > 1) {
        throw new RTGatewayError('cannot pick management address');
    }
    // telnet prompt and mode specification
    switch ($breed = detectDeviceBreed($object_id)) {
        case 'ios12':
        case 'ftos8':
            $protocol = 'netcat';
            // default is netcat mode
            $prompt = '^(Login|[Uu]sername|Password): $|^\\S+[>#]$|\\[[^][]*\\]\\? $';
            // set the prompt in case user would like to specify telnet protocol
            $commands = "terminal length 0\nterminal no monitor\n" . $commands;
            break;
        case 'air12':
            $protocol = 'telnet';
            # Aironet IOS is broken
            $prompt = '^(Username|Password): $|^\\S+[>#]$';
            $commands = "terminal length 0\nterminal no monitor\n" . $commands;
            break;
        case 'fdry5':
            $protocol = 'netcat';
            // default is netcat mode
            $prompt = '^(Login|Username|Password): $|^\\S+[>#]$';
            // set the prompt in case user would like to specify telnet protocol
            $commands = "skip-page-display\n" . $commands;
            break;
        case 'vrp55':
        case 'vrp85':
            $commands = "screen-length 0 temporary\n" . $commands;
            /* fall-through */
        /* fall-through */
        case 'vrp53':
            $protocol = 'telnet';
            $prompt = '^\\[[^[\\]]+\\]$|^<[^<>]+>$|^(Username|Password):$|\\[[Yy][^\\[\\]]*\\]\\s*:?\\s*$';
            break;
        case 'nxos4':
            $protocol = 'telnet';
            $prompt = '(^([Ll]ogin|[Pp]assword):|[>#]) $';
            $commands = "terminal length 0\nterminal no monitor\n" . $commands;
            break;
        case 'xos12':
            $protocol = 'telnet';
            $prompt = ': $|\\.\\d+ # $|\\?\\s*\\([Yy]\\/[Nn]\\)\\s*$';
            $commands = "disable clipaging\n" . $commands;
            break;
        case 'jun10':
            $protocol = 'telnet';
            $prompt = '^login: $|^Password:$|^\\S+@\\S+[>#] $';
            $commands = "set cli screen-length 0\n" . $commands;
            break;
        case 'eos4':
            $protocol = 'telnet';
            # strict RFC854 implementation, netcat won't work
            $prompt = '^\\xf2?(login|Username|Password): $|^\\S+[>#]$';
            $commands = "enable\nno terminal monitor\nterminal length 0\n" . $commands;
            break;
        case 'ros11':
            $protocol = 'netcat';
            # see ftos8 case
            $prompt = '^(User Name|\\rPassword):$|^\\r?\\S+# $';
            $commands = "terminal datadump\n" . $commands;
            $commands .= "\n\n";
            # temporary workaround for telnet server
            break;
        case 'iosxr4':
            $protocol = 'telnet';
            $prompt = '^\\r?(Login|Username|Password): $|^\\r?\\S+[>#]$';
            $commands = "terminal length 0\nterminal monitor disable\n" . $commands;
            break;
        case 'ucs':
            $protocol = 'ucssdk';
            break;
        case 'dlink':
            $protocol = 'netcat';
            $commands = "disable clipaging\n" . $commands;
            break;
    }
    if (!isset($protocol)) {
        $protocol = 'netcat';
    }
    if (!isset($prompt)) {
        $prompt = NULL;
    }
    // set the default settings before calling user-defined callback
    $settings = array('hostname' => $endpoints[0], 'protocol' => $protocol, 'port' => NULL, 'prompt' => $prompt, 'username' => NULL, 'password' => NULL, 'timeout' => 15, 'connect_timeout' => 2, 'prompt_delay' => 0.001, 'sudo_user' => NULL, 'identity_file' => NULL);
    // override default settings
    if (is_callable('terminal_settings')) {
        call_user_func('terminal_settings', $objectInfo, array(&$settings));
    }
    // make gateway-specific CLI params out of settings
    $params = callHook('makeGatewayParams', $object_id, $tolerate_remote_errors, array(&$settings), array(&$commands));
    // call gateway
    $ret_code = callScript($settings['protocol'], $params, $commands, $out, $errors);
    if ($settings['protocol'] != 'ssh' || !$tolerate_remote_errors) {
        if (!empty($errors)) {
            throw new RTGatewayError("{$settings['protocol']} error: " . rtrim($errors));
        } elseif ($ret_code !== 0) {
            throw new RTGatewayError("{$settings['protocol']} error: result code {$ret_code}");
        }
    } elseif (!empty($errors)) {
        // ssh and tolerate and non-empty $errors
        foreach (explode("\n", $errors) as $line) {
            if (strlen($line)) {
                showWarning("{$settings['protocol']} {$settings['hostname']}: {$line}");
            }
        }
    }
    return strtr($out, array("\r" => ""));
    // cut ^M symbols
}
예제 #14
0
function renderRSPoolServerForm($pool_id)
{
    global $nextorder;
    $poolInfo = spotEntity('ipv4rspool', $pool_id);
    if ($poolInfo['rscount']) {
        startPortlet("Manage RS list ({$poolInfo['rscount']})");
        echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
        echo "<tr><th>&nbsp;</th><th>Address</th><th>Port</th><th>Comment</th><th>in service</th><th>configuration</th><th>&nbsp;</th></tr>\n";
        // new RS form
        printOpFormIntro('addRS');
        echo "<tr class=row_odd valign=top><td>";
        printImageHREF('add', 'Add new real server');
        echo "</td><td><input type=text name=rsip></td>";
        $default_port = getConfigVar('DEFAULT_SLB_RS_PORT');
        if ($default_port == 0) {
            $default_port = '';
        }
        echo "<td><input type=text name=rsport size=5 value='{$default_port}'></td>";
        echo "<td><input type=text name=comment size=15></td>";
        $checked = getConfigVar('DEFAULT_IPV4_RS_INSERVICE') == 'yes' ? 'checked' : '';
        echo "<td><input type=checkbox name=inservice {$checked}></td>";
        echo "<td><textarea name=rsconfig></textarea></td><td>";
        printImageHREF('ADD', 'Add new real server', TRUE);
        echo "</td></tr></form>\n";
        $order = 'even';
        foreach (getRSListInPool($pool_id) as $rsid => $rs) {
            printOpFormIntro('updRS', array('rs_id' => $rsid));
            echo "<tr valign=top class=row_{$order}><td><a href='" . makeHrefProcess(array('op' => 'delRS', 'pool_id' => $pool_id, 'id' => $rsid)) . "'>";
            printImageHREF('delete', 'Delete this real server');
            echo "</td><td><input type=text name=rsip value='{$rs['rsip']}'></td>";
            echo "<td><input type=text name=rsport size=5 value='{$rs['rsport']}'></td>";
            echo "<td><input type=text name=comment size=15 value='{$rs['comment']}'></td>";
            $checked = $rs['inservice'] == 'yes' ? 'checked' : '';
            echo "<td><input type=checkbox name=inservice {$checked}></td>";
            echo "<td><textarea name=rsconfig>{$rs['rsconfig']}</textarea></td><td>";
            printImageHREF('SAVE', 'Save changes', TRUE);
            echo "</td></tr></form>\n";
            $order = $nextorder[$order];
        }
        echo "</table>\n";
        finishPortlet();
    }
    startPortlet('Add many');
    printOpFormIntro('addMany');
    echo "<table border=0 align=center>\n<tr><td>";
    if (getConfigVar('DEFAULT_IPV4_RS_INSERVICE') == 'yes') {
        printImageHREF('inservice', 'in service');
    } else {
        printImageHREF('notinservice', 'NOT in service');
    }
    echo "</td><td>Format: ";
    $formats = callHook('getBulkRealsFormats');
    printSelect($formats, array('name' => 'format'));
    echo "</td><td><input type=submit value=Parse></td></tr>\n";
    echo "<tr><td colspan=3><textarea name=rawtext cols=100 rows=25></textarea></td></tr>\n";
    echo "</table>\n";
    finishPortlet();
}
예제 #15
0
function exec8021QDeploy($object_id, $do_push)
{
    global $dbxlink;
    $nsaved = $npushed = $nsaved_uplinks = 0;
    $dbxlink->beginTransaction();
    if (NULL === ($vswitch = getVLANSwitchInfo($object_id, 'FOR UPDATE'))) {
        throw new InvalidArgException('object_id', $object_id, 'VLAN domain is not set for this object');
    }
    $D = getStored8021QConfig($vswitch['object_id'], 'desired');
    $C = getStored8021QConfig($vswitch['object_id'], 'cached');
    try {
        $R = getRunning8021QConfig($vswitch['object_id']);
    } catch (RTGatewayError $e) {
        usePreparedExecuteBlade('UPDATE VLANSwitch SET last_errno=?, last_error_ts=NOW() WHERE object_id=?', array(E_8021Q_PULL_REMOTE_ERROR, $vswitch['object_id']));
        $dbxlink->commit();
        return 0;
    }
    $conflict = FALSE;
    $ok_to_push = array();
    foreach (get8021QSyncOptions($vswitch, $D, $C, $R['portdata']) as $pn => $port) {
        // always update cache with new data from switch
        switch ($port['status']) {
            case 'ok_to_merge':
                // FIXME: this can be logged
                upd8021QPort('cached', $vswitch['object_id'], $pn, $port['both']);
                break;
            case 'ok_to_delete':
                del8021QPort($vswitch['object_id'], $pn);
                $nsaved++;
                break;
            case 'ok_to_add':
                add8021QPort($vswitch['object_id'], $pn, $port['right']);
                $nsaved++;
                break;
            case 'delete_conflict':
            case 'merge_conflict':
            case 'add_conflict':
            case 'martian_conflict':
                $conflict = TRUE;
                break;
            case 'ok_to_pull':
                // FIXME: this can be logged
                upd8021QPort('desired', $vswitch['object_id'], $pn, $port['right']);
                upd8021QPort('cached', $vswitch['object_id'], $pn, $port['right']);
                $nsaved++;
                break;
            case 'ok_to_push_with_merge':
                upd8021QPort('cached', $vswitch['object_id'], $pn, $port['right']);
                // fall through
            // fall through
            case 'ok_to_push':
                $ok_to_push[$pn] = $port['left'];
                break;
        }
    }
    // redo uplinks unconditionally
    $domain_vlanlist = getDomainVLANs($vswitch['domain_id']);
    $Dnew = apply8021QOrder($vswitch['template_id'], getStored8021QConfig($vswitch['object_id'], 'desired'));
    // Take new "desired" configuration and derive uplink port configuration
    // from it. Then cancel changes to immune VLANs and save resulting
    // changes (if any left).
    $new_uplinks = filter8021QChangeRequests($domain_vlanlist, $Dnew, produceUplinkPorts($domain_vlanlist, $Dnew, $vswitch['object_id']));
    $nsaved_uplinks += replace8021QPorts('desired', $vswitch['object_id'], $Dnew, $new_uplinks);
    if ($nsaved + $nsaved_uplinks) {
        // saved configuration has changed (either "user" ports have changed,
        // or uplinks, or both), so bump revision number up)
        touchVLANSwitch($vswitch['object_id']);
    }
    if ($conflict) {
        usePreparedExecuteBlade('UPDATE VLANSwitch SET out_of_sync="yes", last_errno=?, last_error_ts=NOW() WHERE object_id=?', array(E_8021Q_VERSION_CONFLICT, $vswitch['object_id']));
    } else {
        usePreparedExecuteBlade('UPDATE VLANSwitch SET last_errno=?, last_error_ts=NOW() WHERE object_id=?', array(E_8021Q_NOERROR, $vswitch['object_id']));
        // Modified uplinks are very likely to differ from those in R-copy,
        // so don't mark device as clean, if this happened. This can cost
        // us an additional, empty round of sync, but at least out_of_sync
        // won't be mistakenly set to 'no'.
        // FIXME: A cleaner way of coupling pull and push operations would
        // be to split this function into two.
        if (!count($ok_to_push) and !$nsaved_uplinks) {
            usePreparedExecuteBlade('UPDATE VLANSwitch SET out_of_sync="no" WHERE object_id=?', array($vswitch['object_id']));
        } elseif ($do_push) {
            usePreparedExecuteBlade('UPDATE VLANSwitch SET last_push_started=NOW() WHERE object_id=?', array($vswitch['object_id']));
            try {
                $vlan_names = isset($R['vlannames']) ? $R['vlannames'] : array();
                $npushed += exportSwitch8021QConfig($vswitch, $R['vlanlist'], $R['portdata'], $ok_to_push, $vlan_names);
                // update cache for ports deployed
                replace8021QPorts('cached', $vswitch['object_id'], $R['portdata'], $ok_to_push);
                usePreparedExecuteBlade('UPDATE VLANSwitch SET last_push_finished=NOW(), out_of_sync="no", last_errno=? WHERE object_id=?', array(E_8021Q_NOERROR, $vswitch['object_id']));
            } catch (RTGatewayError $r) {
                usePreparedExecuteBlade('UPDATE VLANSwitch SET out_of_sync="yes", last_error_ts=NOW(), last_errno=? WHERE object_id=?', array(E_8021Q_PUSH_REMOTE_ERROR, $vswitch['object_id']));
                callHook('pushErrorHandler', $object_id, $r);
            }
        }
    }
    $dbxlink->commit();
    // start downlink work only after unlocking current object to make deadlocks less likely to happen
    // TODO: only process changed uplink ports
    if ($nsaved_uplinks) {
        initiateUplinksReverb($vswitch['object_id'], $new_uplinks);
    }
    return $nsaved + $npushed + $nsaved_uplinks;
}