2017-05-16 13:34:00 +02:00
|
|
|
|
2018-08-05 15:53:46 +02:00
|
|
|
import _ from 'underscore'
|
2018-07-23 19:55:26 +02:00
|
|
|
import {debounce} from "underscore"
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
import React from 'react'
|
|
|
|
import {connect} from 'react-redux'
|
|
|
|
|
|
|
|
import {Link} from 'react-router'
|
2018-07-23 19:55:26 +02:00
|
|
|
import {push} from 'react-router-redux'
|
2017-05-16 13:34:00 +02:00
|
|
|
|
|
|
|
import Details from '../details'
|
|
|
|
import Status from '../status'
|
|
|
|
import PageHeader from 'components/page-header'
|
|
|
|
|
2018-07-27 18:18:48 +02:00
|
|
|
import {apiCacheStatus} from 'components/api-status/cache'
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
import ProtocolName
|
|
|
|
from 'components/routeservers/protocols/name'
|
|
|
|
|
|
|
|
|
|
|
|
import SearchInput from 'components/search-input'
|
|
|
|
|
2018-08-05 15:53:46 +02:00
|
|
|
import RoutesView from './view'
|
|
|
|
import QuickLinks from './quick-links'
|
|
|
|
import RelatedPeers from './related-peers'
|
2018-08-05 12:27:42 +02:00
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
import BgpAttributesModal
|
|
|
|
from './bgp-attributes-modal'
|
|
|
|
|
2018-07-16 22:53:57 +02:00
|
|
|
|
|
|
|
import RoutesLoadingIndicator from './loading-indicator'
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
// Actions
|
2018-07-23 19:55:26 +02:00
|
|
|
import {setFilterQueryValue}
|
|
|
|
from './actions'
|
2017-05-16 13:34:00 +02:00
|
|
|
import {loadRouteserverProtocol}
|
|
|
|
from 'components/routeservers/actions'
|
|
|
|
|
2018-07-16 10:52:41 +02:00
|
|
|
|
|
|
|
// Constants
|
|
|
|
import {ROUTES_RECEIVED,
|
|
|
|
ROUTES_FILTERED,
|
|
|
|
ROUTES_NOT_EXPORTED} from './actions';
|
|
|
|
|
|
|
|
|
2018-08-03 18:25:28 +02:00
|
|
|
const makeQueryLinkProps = function(routing, query, loadNotExported) {
|
|
|
|
// Load not exported routes flag
|
|
|
|
const ne = loadNotExported ? 1 : 0;
|
|
|
|
|
2018-07-23 19:55:26 +02:00
|
|
|
// As we need to reset the pagination, we can just
|
2018-08-03 18:25:28 +02:00
|
|
|
// ommit these other parameters and just use pathname + query + ne
|
2018-07-23 19:55:26 +02:00
|
|
|
return {
|
|
|
|
pathname: routing.pathname,
|
2018-08-03 18:25:28 +02:00
|
|
|
search: `?ne=${ne}&q=${query}`
|
2018-07-23 19:55:26 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the routes view is empty, (while nothing is,
|
|
|
|
* loading) and show info screen.
|
|
|
|
*/
|
|
|
|
const RoutesViewEmpty = (props) => {
|
|
|
|
const isLoading = props.routes.received.loading ||
|
|
|
|
props.routes.filtered.loading ||
|
|
|
|
props.routes.notExported.loading;
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
return null; // We are not a loading indicator.
|
|
|
|
}
|
2018-08-03 19:26:49 +02:00
|
|
|
|
|
|
|
if (!props.loadNotExported) {
|
|
|
|
return null; // There may be routes matching the query in there!
|
|
|
|
}
|
2018-07-23 19:55:26 +02:00
|
|
|
|
|
|
|
const hasContent = props.routes.received.totalResults > 0 ||
|
|
|
|
props.routes.filtered.totalResults > 0 ||
|
|
|
|
props.routes.notExported.totalResults > 0;
|
|
|
|
if (hasContent) {
|
|
|
|
return null; // Nothing to do then.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Show info screen
|
|
|
|
return (
|
|
|
|
<div className="card info-result-empty">
|
|
|
|
<h4>No routes found matching your query.</h4>
|
|
|
|
<p>Please check if your query is too restrictive.</p>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
class RoutesPage extends React.Component {
|
2018-07-23 19:55:26 +02:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
// Create debounced dispatch, as we don't want to flood
|
|
|
|
// the server with API queries
|
|
|
|
this.debouncedDispatch = debounce(this.props.dispatch, 350);
|
|
|
|
}
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
|
|
|
|
setFilter(value) {
|
|
|
|
this.props.dispatch(
|
2018-07-23 19:55:26 +02:00
|
|
|
setFilterQueryValue(value)
|
2017-05-16 13:34:00 +02:00
|
|
|
);
|
2018-07-23 19:55:26 +02:00
|
|
|
|
|
|
|
this.debouncedDispatch(push(makeQueryLinkProps(
|
2018-08-03 18:25:28 +02:00
|
|
|
this.props.routing, value, this.props.loadNotExported
|
2018-07-23 19:55:26 +02:00
|
|
|
)));
|
2017-05-16 13:34:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
2018-07-15 17:52:44 +02:00
|
|
|
// Assert neighbors for RS are loaded
|
2017-05-16 13:34:00 +02:00
|
|
|
this.props.dispatch(
|
|
|
|
loadRouteserverProtocol(parseInt(this.props.params.routeserverId))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2018-07-27 18:18:48 +02:00
|
|
|
let cacheStatus = apiCacheStatus(this.props.routes.received.apiStatus);
|
|
|
|
if (this.props.anyLoading) {
|
|
|
|
cacheStatus = null;
|
|
|
|
}
|
|
|
|
|
2018-08-05 15:53:46 +02:00
|
|
|
// We have to shift the layout a bit, to make room for
|
|
|
|
// the related peers tabs
|
|
|
|
let pageClass = "routeservers-page";
|
|
|
|
if (this.props.relatedPeers.length > 1) {
|
|
|
|
pageClass += " has-related-peers";
|
|
|
|
}
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
return(
|
2018-08-05 15:53:46 +02:00
|
|
|
<div className={pageClass}>
|
2017-05-16 13:34:00 +02:00
|
|
|
<PageHeader>
|
|
|
|
<Link to={`/routeservers/${this.props.params.routeserverId}`}>
|
|
|
|
<Details routeserverId={this.props.params.routeserverId} />
|
|
|
|
</Link>
|
|
|
|
<span className="spacer">»</span>
|
|
|
|
<ProtocolName routeserverId={this.props.params.routeserverId}
|
|
|
|
protocolId={this.props.params.protocolId} />
|
|
|
|
</PageHeader>
|
|
|
|
|
|
|
|
<BgpAttributesModal />
|
|
|
|
|
|
|
|
<div className="row details-main">
|
|
|
|
<div className="col-md-8">
|
|
|
|
|
|
|
|
<div className="card">
|
2018-08-05 15:53:46 +02:00
|
|
|
<RelatedPeers peers={this.props.relatedPeers}
|
|
|
|
protocolId={this.props.params.protocolId}
|
|
|
|
routeserverId={this.props.params.routeserverId} />
|
2017-05-16 13:34:00 +02:00
|
|
|
<SearchInput
|
2018-07-23 19:55:26 +02:00
|
|
|
value={this.props.filterValue}
|
2018-03-16 18:12:53 +01:00
|
|
|
placeholder="Filter by Network or BGP next-hop"
|
2017-05-16 13:34:00 +02:00
|
|
|
onChange={(e) => this.setFilter(e.target.value)} />
|
|
|
|
</div>
|
|
|
|
|
2018-08-05 12:27:42 +02:00
|
|
|
<QuickLinks routes={this.props.routes} />
|
|
|
|
|
2018-08-03 19:26:49 +02:00
|
|
|
<RoutesViewEmpty routes={this.props.routes}
|
|
|
|
loadNotExported={this.props.loadNotExported} />
|
2018-07-23 19:55:26 +02:00
|
|
|
|
2018-07-15 17:52:44 +02:00
|
|
|
<RoutesView
|
2018-07-16 22:53:57 +02:00
|
|
|
type={ROUTES_FILTERED}
|
2018-07-15 17:52:44 +02:00
|
|
|
routeserverId={this.props.params.routeserverId}
|
|
|
|
protocolId={this.props.params.protocolId} />
|
|
|
|
|
|
|
|
<RoutesView
|
2018-07-16 22:53:57 +02:00
|
|
|
type={ROUTES_RECEIVED}
|
2018-07-15 17:52:44 +02:00
|
|
|
routeserverId={this.props.params.routeserverId}
|
|
|
|
protocolId={this.props.params.protocolId} />
|
|
|
|
|
|
|
|
<RoutesView
|
2018-07-16 10:52:41 +02:00
|
|
|
type={ROUTES_NOT_EXPORTED}
|
2018-07-15 17:52:44 +02:00
|
|
|
routeserverId={this.props.params.routeserverId}
|
|
|
|
protocolId={this.props.params.protocolId} />
|
|
|
|
|
2018-07-16 22:53:57 +02:00
|
|
|
<RoutesLoadingIndicator />
|
|
|
|
|
2017-05-16 13:34:00 +02:00
|
|
|
</div>
|
|
|
|
<div className="col-md-4">
|
|
|
|
<div className="card">
|
2018-07-27 18:18:48 +02:00
|
|
|
<Status routeserverId={this.props.params.routeserverId}
|
|
|
|
cacheStatus={cacheStatus} />
|
2017-05-16 13:34:00 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default connect(
|
2018-08-05 15:53:46 +02:00
|
|
|
(state, props) => {
|
|
|
|
const protocolId = props.params.protocolId;
|
|
|
|
const rsId = parseInt(props.params.routeserverId, 10);
|
|
|
|
const neighbors = state.routeservers.protocols[rsId];
|
|
|
|
const neighbor = _.findWhere(neighbors, {id: protocolId});
|
|
|
|
|
|
|
|
// Find related peers. Peers belonging to the same AS.
|
|
|
|
let relatedPeers = [];
|
|
|
|
if (neighbor) {
|
|
|
|
relatedPeers = _.where(neighbors, {asn: neighbor.asn});
|
|
|
|
}
|
|
|
|
|
2018-07-16 10:52:41 +02:00
|
|
|
let received = {
|
|
|
|
loading: state.routes.receivedLoading,
|
|
|
|
totalResults: state.routes.receivedTotalResults,
|
2018-07-27 18:18:48 +02:00
|
|
|
apiStatus: state.routes.receivedApiStatus
|
2018-07-16 10:52:41 +02:00
|
|
|
};
|
|
|
|
let filtered = {
|
|
|
|
loading: state.routes.filteredLoading,
|
|
|
|
totalResults: state.routes.filteredTotalResults,
|
2018-07-27 18:18:48 +02:00
|
|
|
apiStatus: state.routes.filteredApiStatus
|
2018-07-16 10:52:41 +02:00
|
|
|
};
|
|
|
|
let notExported = {
|
|
|
|
loading: state.routes.notExportedLoading,
|
|
|
|
totalResults: state.routes.notExportedTotalResults,
|
2018-07-27 18:18:48 +02:00
|
|
|
apiStatus: state.routes.notExportedApiStatus
|
2018-07-16 10:52:41 +02:00
|
|
|
};
|
2018-07-27 18:18:48 +02:00
|
|
|
let anyLoading = state.routes.receivedLoading ||
|
|
|
|
state.routes.filteredLoading ||
|
|
|
|
state.routes.notExportedLoading;
|
2018-07-16 10:52:41 +02:00
|
|
|
return({
|
2018-07-23 19:55:26 +02:00
|
|
|
filterValue: state.routes.filterValue,
|
2018-07-16 10:52:41 +02:00
|
|
|
routes: {
|
2018-07-16 19:27:08 +02:00
|
|
|
[ROUTES_RECEIVED]: received,
|
|
|
|
[ROUTES_FILTERED]: filtered,
|
|
|
|
[ROUTES_NOT_EXPORTED]: notExported
|
2018-07-23 19:55:26 +02:00
|
|
|
},
|
2018-07-27 18:18:48 +02:00
|
|
|
routing: state.routing.locationBeforeTransitions,
|
2018-08-03 19:26:49 +02:00
|
|
|
loadNotExported: state.routes.loadNotExported ||
|
|
|
|
!state.config.noexport_load_on_demand,
|
|
|
|
|
2018-08-05 15:53:46 +02:00
|
|
|
anyLoading: anyLoading,
|
|
|
|
|
|
|
|
relatedPeers: relatedPeers
|
2018-07-16 10:52:41 +02:00
|
|
|
});
|
2017-05-16 13:34:00 +02:00
|
|
|
}
|
|
|
|
)(RoutesPage);
|
2018-03-16 15:41:14 +01:00
|
|
|
|