mirror of
https://github.com/alice-lg/alice-lg.git
synced 2024-05-11 05:55:03 +00:00
Merge branch 'feature/cache-information' into develop
This commit is contained in:
@ -59,7 +59,7 @@ type ThemeConfig struct {
|
||||
|
||||
type PaginationConfig struct {
|
||||
RoutesFilteredPageSize int `ini:"routes_filtered_page_size"`
|
||||
RoutesAcceptedPageSize int `ini:"routes_accpted_page_size"`
|
||||
RoutesAcceptedPageSize int `ini:"routes_accepted_page_size"`
|
||||
RoutesNotExportedPageSize int `ini:"routes_not_exported_page_size"`
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ func parseApiStatus(bird ClientResponse, config Config) (api.ApiStatus, error) {
|
||||
return status, fmt.Errorf(birdErr)
|
||||
}
|
||||
|
||||
// Parse TTL
|
||||
ttl, err := parseServerTime(
|
||||
bird["ttl"],
|
||||
config.ServerTime,
|
||||
@ -62,10 +63,39 @@ func parseApiStatus(bird ClientResponse, config Config) (api.ApiStatus, error) {
|
||||
return api.ApiStatus{}, err
|
||||
}
|
||||
|
||||
// Parse Cache Status
|
||||
cacheStatus, _ := parseCacheStatus(birdApi, config)
|
||||
|
||||
status := api.ApiStatus{
|
||||
Version: birdApi["Version"].(string),
|
||||
ResultFromCache: birdApi["result_from_cache"].(bool),
|
||||
Ttl: ttl,
|
||||
CacheStatus: cacheStatus,
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// Parse cache status from api response
|
||||
func parseCacheStatus(cacheStatus map[string]interface{}, config Config) (api.CacheStatus, error) {
|
||||
cache, ok := cacheStatus["cache_status"].(map[string]interface{})
|
||||
if !ok {
|
||||
return api.CacheStatus{}, fmt.Errorf("Invalid Cache Status")
|
||||
}
|
||||
|
||||
cachedAt, ok := cache["cached_at"].(map[string]interface{})
|
||||
if !ok {
|
||||
return api.CacheStatus{}, fmt.Errorf("Invalid Cache Status")
|
||||
}
|
||||
|
||||
cachedAtTime, err := parseServerTime(cachedAt["date"], config.ServerTime, config.Timezone)
|
||||
if err != nil {
|
||||
return api.CacheStatus{}, err
|
||||
}
|
||||
|
||||
status := api.CacheStatus{
|
||||
CachedAt: cachedAtTime,
|
||||
// We ommit OrigTTL for now...
|
||||
}
|
||||
|
||||
return status, nil
|
||||
|
@ -210,10 +210,14 @@ func (self *Birdwatcher) Routes(neighbourId string) (*api.RoutesResponse, error)
|
||||
|
||||
gateway := ""
|
||||
learnt_from := ""
|
||||
if len(imported) > 0 { // infer next_hop ip address from imported[0]
|
||||
gateway = imported[0].Gateway //TODO: change mechanism to infer gateway when state becomes available elsewhere.
|
||||
learnt_from = mustString(imported[0].Details["learnt_from"], gateway) // also take learnt_from address into account if present.
|
||||
// ^ learnt_from is regularly present on routes for remote-triggered blackholing or on filtered routes (e.g. next_hop not in AS-Set)
|
||||
if len(imported) > 0 {
|
||||
// infer next_hop ip address from imported[0]
|
||||
gateway = imported[0].Gateway
|
||||
//TODO: change mechanism to infer gateway when state becomes available elsewhere.
|
||||
learnt_from = mustString(imported[0].Details["learnt_from"], gateway)
|
||||
// also take learnt_from address into account if present.
|
||||
// ^ learnt_from is regularly present on routes for remote-triggered
|
||||
// blackholing or on filtered routes (e.g. next_hop not in AS-Set)
|
||||
}
|
||||
|
||||
// Optional: Filtered
|
||||
@ -233,7 +237,9 @@ func (self *Birdwatcher) Routes(neighbourId string) (*api.RoutesResponse, error)
|
||||
}
|
||||
// choose routes with next_hop == gateway of this neighbour
|
||||
for _, route := range filtered {
|
||||
if (route.Gateway == gateway) || (route.Gateway == learnt_from) || (route.Details["learnt_from"] == gateway) {
|
||||
if (route.Gateway == gateway) ||
|
||||
(route.Gateway == learnt_from) ||
|
||||
(route.Details["learnt_from"] == gateway) {
|
||||
result_filtered = append(result_filtered, route)
|
||||
delete(importedMap, route.Id) // remove routes that are filtered on pipe
|
||||
} else if len(imported) == 0 { // in case there are just filtered routes
|
||||
|
@ -19,18 +19,15 @@
|
||||
}
|
||||
|
||||
.routeserver-status {
|
||||
ul {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
li {
|
||||
padding: 10px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
td {
|
||||
vertical-align: top;
|
||||
padding: 4px;
|
||||
|
||||
i {
|
||||
width: 25px;
|
||||
}
|
||||
i {
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
30
client/components/api-status/cache.jsx
Normal file
30
client/components/api-status/cache.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import {parseServerTime} from 'components/datetime/parse'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
/*
|
||||
* Calculate age (generated_at), and set from_cache_status
|
||||
*/
|
||||
export const apiCacheStatus = function(apiStatus) {
|
||||
if (apiStatus == {}) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cacheStatus = apiStatus["cache_status"] || {};
|
||||
const cachedAt = cacheStatus.cached_at;
|
||||
if (!cachedAt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fromCache = apiStatus.result_from_cache;
|
||||
const ttl = parseServerTime(apiStatus.ttl);
|
||||
const generatedAt = parseServerTime(cachedAt);
|
||||
const age = ttl.diff(generatedAt); // ms
|
||||
|
||||
return {fromCache, age, generatedAt, ttl};
|
||||
};
|
||||
|
@ -10,6 +10,8 @@ import React from 'react'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
import {parseServerTime} from './parse'
|
||||
|
||||
|
||||
export default class Datetime extends React.Component {
|
||||
render() {
|
||||
@ -18,7 +20,7 @@ export default class Datetime extends React.Component {
|
||||
timefmt = 'LLLL';
|
||||
}
|
||||
|
||||
let time = moment(this.props.value);
|
||||
let time = parseServerTime(this.props.value);
|
||||
return (
|
||||
<span>{time.format(timefmt)}</span>
|
||||
);
|
||||
|
16
client/components/datetime/parse.jsx
Normal file
16
client/components/datetime/parse.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
/*
|
||||
* Some datetime parsing helper functions
|
||||
*/
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
|
||||
window.moment = moment;
|
||||
|
||||
export function parseServerTime(serverTime) {
|
||||
const fmt = "YYYY-MM-DDTHH:mm:ss.SSSSSSSSZ"; // S was 4 byte short
|
||||
return moment(serverTime, fmt);
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@ window.momnet = moment;
|
||||
|
||||
export default class RelativeTimestamp extends React.Component {
|
||||
render() {
|
||||
|
||||
let now = moment.utc()
|
||||
let rel = moment(now._d.getTime() - (this.props.value / 1000.0 / 1000.0))
|
||||
const tsMs = this.props.value / 1000.0 / 1000.0; // nano -> micro -> milli
|
||||
const now = moment.utc()
|
||||
const rel = now.subtract(tsMs, 'ms');
|
||||
|
||||
return (
|
||||
<span>{rel.fromNow(this.props.suffix)}</span>
|
||||
|
@ -11,6 +11,8 @@ import Details from '../details'
|
||||
import Status from '../status'
|
||||
import PageHeader from 'components/page-header'
|
||||
|
||||
import {apiCacheStatus} from 'components/api-status/cache'
|
||||
|
||||
import ProtocolName
|
||||
from 'components/routeservers/protocols/name'
|
||||
|
||||
@ -106,6 +108,11 @@ class RoutesPage extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let cacheStatus = apiCacheStatus(this.props.routes.received.apiStatus);
|
||||
if (this.props.anyLoading) {
|
||||
cacheStatus = null;
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="routeservers-page">
|
||||
<PageHeader>
|
||||
@ -151,7 +158,8 @@ class RoutesPage extends React.Component {
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<div className="card">
|
||||
<Status routeserverId={this.props.params.routeserverId} />
|
||||
<Status routeserverId={this.props.params.routeserverId}
|
||||
cacheStatus={cacheStatus} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -167,15 +175,21 @@ export default connect(
|
||||
let received = {
|
||||
loading: state.routes.receivedLoading,
|
||||
totalResults: state.routes.receivedTotalResults,
|
||||
apiStatus: state.routes.receivedApiStatus
|
||||
};
|
||||
let filtered = {
|
||||
loading: state.routes.filteredLoading,
|
||||
totalResults: state.routes.filteredTotalResults,
|
||||
apiStatus: state.routes.filteredApiStatus
|
||||
};
|
||||
let notExported = {
|
||||
loading: state.routes.notExportedLoading,
|
||||
totalResults: state.routes.notExportedTotalResults,
|
||||
apiStatus: state.routes.notExportedApiStatus
|
||||
};
|
||||
let anyLoading = state.routes.receivedLoading ||
|
||||
state.routes.filteredLoading ||
|
||||
state.routes.notExportedLoading;
|
||||
return({
|
||||
filterValue: state.routes.filterValue,
|
||||
routes: {
|
||||
@ -183,7 +197,8 @@ export default connect(
|
||||
[ROUTES_FILTERED]: filtered,
|
||||
[ROUTES_NOT_EXPORTED]: notExported
|
||||
},
|
||||
routing: state.routing.locationBeforeTransitions
|
||||
routing: state.routing.locationBeforeTransitions,
|
||||
anyLoading: anyLoading
|
||||
});
|
||||
}
|
||||
)(RoutesPage);
|
||||
|
@ -204,7 +204,7 @@ export class RoutesPaginationInfo extends React.Component {
|
||||
const perPage = this.props.pageSize;
|
||||
const start = this.props.page * perPage + 1;
|
||||
const end = Math.min(start + perPage - 1, totalResults);
|
||||
if (this.props.totalPages == 1) {
|
||||
if (this.props.totalPages <= 1) {
|
||||
let routes = "route";
|
||||
if (totalResults > 1) {
|
||||
routes = "routes";
|
||||
|
@ -105,7 +105,7 @@ function _handleFetchRoutesRequest(type, state, payload) {
|
||||
function _handleFetchRoutesSuccess(type, state, payload) {
|
||||
const stype = _stateType(type);
|
||||
const pagination = payload.pagination;
|
||||
const apiStatus = payload.api;
|
||||
const apiStatus = payload.apiStatus;
|
||||
|
||||
let nextState = Object.assign({}, state, {
|
||||
[stype]: payload.routes,
|
||||
|
@ -3,11 +3,13 @@ import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import Datetime from 'components/datetime'
|
||||
import moment from 'moment'
|
||||
|
||||
|
||||
class Details extends React.Component {
|
||||
|
||||
render() {
|
||||
|
||||
let rsStatus = this.props.details[this.props.routeserverId];
|
||||
if (!rsStatus) {
|
||||
return null;
|
||||
@ -19,26 +21,52 @@ class Details extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
let lastReboot = rsStatus.last_reboot;
|
||||
if (lastReboot == "0001-01-01T00:00:00Z") {
|
||||
lastReboot = null;
|
||||
}
|
||||
|
||||
let cacheStatus = null;
|
||||
if (this.props.cacheStatus) {
|
||||
const s = this.props.cacheStatus;
|
||||
cacheStatus = [
|
||||
<tr key="cache-status-cached-at">
|
||||
<td><i className="fa fa-refresh"></i></td>
|
||||
<td>
|
||||
Generated <b>{s.generatedAt.fromNow()}</b><br />
|
||||
Next refresh <b>{s.ttl.fromNow()}</b>
|
||||
</td>
|
||||
</tr>,
|
||||
|
||||
<tr key="cache-status-ttl">
|
||||
<td></td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="routeserver-status">
|
||||
<ul>
|
||||
{lastReboot &&
|
||||
<li><i className="fa fa-clock-o"></i>
|
||||
Last Reboot: <b><Datetime value={lastReboot} /></b>
|
||||
</li>}
|
||||
<li><i className="fa fa-clock-o"></i>
|
||||
Last Reconfig: <b><Datetime value={rsStatus.last_reconfig} /></b>
|
||||
</li>
|
||||
<li><i className="fa fa-thumbs-up"></i>
|
||||
<b>{rsStatus.message}</b></li>
|
||||
</ul>
|
||||
</div>
|
||||
<table className="routeserver-status">
|
||||
<tbody>
|
||||
{lastReboot &&
|
||||
<tr>
|
||||
<td><i className="fa fa-clock-o"></i></td>
|
||||
<td>Last Reboot: <b><Datetime value={lastReboot} /></b></td>
|
||||
</tr>}
|
||||
<tr>
|
||||
<td><i className="fa fa-clock-o"></i></td>
|
||||
<td>Last Reconfig: <b><Datetime value={rsStatus.last_reconfig} /></b></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><i className="fa fa-thumbs-up"></i></td>
|
||||
<td><b>{rsStatus.message}</b></td>
|
||||
</tr>
|
||||
|
||||
{cacheStatus}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user