1
0
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:
Matthias Hannig
2018-07-27 18:21:11 +02:00
12 changed files with 162 additions and 38 deletions

View File

@ -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"`
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View 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};
};

View File

@ -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>
);

View 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);
}

View File

@ -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>

View File

@ -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);

View File

@ -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";

View File

@ -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,

View File

@ -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>
);
}
}