mirror of https://github.com/status-im/consul.git
ui: Initial Server Status Overview Page (#12599)
This commit is contained in:
parent
61af7947f9
commit
18f55be3c4
|
@ -1,6 +0,0 @@
|
|||
import BaseAbility from './base';
|
||||
|
||||
export default class RaftAbility extends BaseAbility {
|
||||
resource = 'operator';
|
||||
segmented = false;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import BaseAbility from './base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ZoneAbility extends BaseAbility {
|
||||
@service('env') env;
|
||||
|
||||
get canRead() {
|
||||
return this.env.var('CONSUL_NSPACES_ENABLED');
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
margin-bottom: calc(var(--padding-y) / 2);
|
||||
}
|
||||
%consul-server-card.voting-status-leader dd {
|
||||
margin-left: calc(var(--tile-size) + var(--padding-x));
|
||||
margin-left: calc(var(--tile-size) + 1rem); /* 16px */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
<ul>
|
||||
{{#each @items as |item|}}
|
||||
<li>
|
||||
<Consul::Server::Card
|
||||
@item={{item}}
|
||||
/>
|
||||
<a href={{href-to 'dc.nodes.show' item.Name}}>
|
||||
<Consul::Server::Card
|
||||
@item={{item}}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
border-color: rgb(var(--tone-gray-999) / 10%);
|
||||
}
|
||||
%with-leader-tile::after {
|
||||
--icon-name: icon-star-circle;
|
||||
--icon-name: icon-star-fill;
|
||||
--icon-size: icon-700;
|
||||
color: rgb(var(--strawberry-500));
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ export default class Datacenter extends Model {
|
|||
@attr('string') Leader;
|
||||
@attr() Voters; // []
|
||||
@attr() Servers; // [] the API uses {} but we reshape that on the frontend
|
||||
@attr() RedundancyZones;
|
||||
@attr() Default; // added by the frontend, {Servers: []} any server that isn't in a zone
|
||||
@attr() ReadReplicas;
|
||||
//
|
||||
@attr('boolean') Local;
|
||||
@attr('boolean') Primary;
|
||||
|
|
|
@ -108,21 +108,56 @@ export default class DcService extends RepositoryService {
|
|||
GET /v1/operator/autopilot/state?${{ dc }}
|
||||
X-Request-ID: ${uri}
|
||||
`)(
|
||||
(headers, body, cache) => ({
|
||||
meta: {
|
||||
version: 2,
|
||||
uri: uri,
|
||||
interval: 30 * SECONDS
|
||||
},
|
||||
body: cache(
|
||||
{
|
||||
...body,
|
||||
// turn servers into an array instead of a map/object
|
||||
Servers: Object.values(body.Servers)
|
||||
(headers, body, cache) => {
|
||||
// turn servers into an array instead of a map/object
|
||||
const servers = Object.values(body.Servers);
|
||||
const grouped = [];
|
||||
return {
|
||||
meta: {
|
||||
version: 2,
|
||||
uri: uri,
|
||||
},
|
||||
uri => uri`${MODEL_NAME}:///${''}/${''}/${dc}/datacenter`
|
||||
)
|
||||
})
|
||||
body: cache(
|
||||
{
|
||||
...body,
|
||||
// all servers
|
||||
Servers: servers,
|
||||
RedundancyZones: Object.entries(body.RedundancyZones || {}).map(([key, value]) => {
|
||||
const zone = {
|
||||
...value,
|
||||
Name: key,
|
||||
Healthy: true,
|
||||
// convert the string[] to Server[]
|
||||
Servers: value.Servers.reduce((prev, item) => {
|
||||
const server = body.Servers[item];
|
||||
// TODO: It is not currently clear whether we should be
|
||||
// taking ReadReplicas out of the RedundancyZones when we
|
||||
// encounter one in a Zone once this is cleared up either
|
||||
// way we can either remove this comment or make any
|
||||
// necessary amends here
|
||||
if(!server.ReadReplica) {
|
||||
// keep a record of things
|
||||
grouped.push(server.ID);
|
||||
prev.push(server);
|
||||
}
|
||||
return prev;
|
||||
}, []),
|
||||
}
|
||||
return zone;
|
||||
}),
|
||||
ReadReplicas: (body.ReadReplicas || []).map(item => {
|
||||
// keep a record of things
|
||||
grouped.push(item);
|
||||
return body.Servers[item];
|
||||
}),
|
||||
Default: {
|
||||
Servers: servers.filter(item => !grouped.includes(item.ID))
|
||||
}
|
||||
},
|
||||
uri => uri`${MODEL_NAME}:///${''}/${''}/${dc}/datacenter`
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
}
|
||||
%visually-unhidden,
|
||||
%unvisually-hidden {
|
||||
position: static;
|
||||
clip: unset;
|
||||
overflow: visible;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: static !important;
|
||||
clip: unset !important;
|
||||
overflow: visible !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
%visually-hidden-text {
|
||||
text-indent: -9000px;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
--decor-border-400: 4px solid;
|
||||
|
||||
/* box-shadowing*/
|
||||
--decor-elevation-000: none;
|
||||
--decor-elevation-100: 0 3px 2px rgb(var(--black) / 6%);
|
||||
--decor-elevation-200: 0 2px 4px rgb(var(--black) / 10%);
|
||||
--decor-elevation-300: 0 5px 1px -2px rgb(var(--black) / 12%);
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
@import 'routes/dc/kv/index';
|
||||
@import 'routes/dc/acls/index';
|
||||
@import 'routes/dc/intentions/index';
|
||||
@import 'routes/dc/overview/serverstatus';
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
section[data-route='dc.show.serverstatus'] {
|
||||
@extend %serverstatus-route;
|
||||
}
|
||||
%serverstatus-route .server-failure-tolerance {
|
||||
@extend %server-failure-tolerance;
|
||||
}
|
||||
%serverstatus-route .redundancy-zones {
|
||||
@extend %redundancy-zones;
|
||||
}
|
||||
%redundancy-zones section {
|
||||
@extend %redundancy-zone;
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
%serverstatus-route h2,
|
||||
%serverstatus-route h3 {
|
||||
@extend %h200;
|
||||
}
|
||||
|
||||
%server-failure-tolerance {
|
||||
@extend %panel;
|
||||
box-shadow: var(--decor-elevation-000);
|
||||
padding: var(--padding-y) var(--padding-x);
|
||||
width: 770px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
%server-failure-tolerance > header {
|
||||
width: 100%;
|
||||
padding-bottom: 0.500rem; /* 8px */
|
||||
margin-bottom: 1rem; /* 16px */
|
||||
border-bottom: var(--decor-border-100);
|
||||
border-color: rgb(var(--tone-border));
|
||||
}
|
||||
%server-failure-tolerance header em {
|
||||
@extend %pill-200;
|
||||
font-size: 0.812rem; /* 13px */
|
||||
background-color: rgb(var(--tone-gray-200));
|
||||
|
||||
text-transform: uppercase;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
%server-failure-tolerance > section {
|
||||
width: 50%;
|
||||
}
|
||||
%server-failure-tolerance > section,
|
||||
%server-failure-tolerance dl {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
%server-failure-tolerance dl {
|
||||
flex-grow: 1;
|
||||
justify-content: space-between;
|
||||
}
|
||||
%server-failure-tolerance dd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
%server-failure-tolerance dl.warning dd::before {
|
||||
--icon-name: icon-alert-circle;
|
||||
--icon-resolution: .5;
|
||||
--icon-size: icon-800;
|
||||
--icon-color: rgb(var(--tone-orange-400));
|
||||
content: '';
|
||||
margin-right: 0.500rem; /* 8px */
|
||||
}
|
||||
%server-failure-tolerance section:first-of-type dl {
|
||||
padding-right: 1.500rem; /* 24px */
|
||||
}
|
||||
%server-failure-tolerance dt {
|
||||
@extend %p2;
|
||||
color: rgb(var(--tone-gray-700));
|
||||
}
|
||||
%server-failure-tolerance dd {
|
||||
font-size: var(--typo-size-250);
|
||||
color: rgb(var(--tone-gray-999));
|
||||
}
|
||||
%server-failure-tolerance header span::before {
|
||||
--icon-name: icon-info;
|
||||
--icon-size: icon-300;
|
||||
--icon-color: rgb(var(--tone-gray-500));
|
||||
vertical-align: unset;
|
||||
content: '';
|
||||
}
|
||||
|
||||
%serverstatus-route section:not([class*='-tolerance']) h2 {
|
||||
margin-top: 1.5rem; /* 24px */
|
||||
margin-bottom: 1.5rem; /* 24px */
|
||||
}
|
||||
%serverstatus-route section:not([class*='-tolerance']) header {
|
||||
margin-top: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
|
||||
%redundancy-zones h3 {
|
||||
@extend %h300;
|
||||
}
|
||||
%redundancy-zone header {
|
||||
display: flow-root;
|
||||
}
|
||||
%redundancy-zone header h3 {
|
||||
float: left;
|
||||
margin-right: 0.5rem; /* 8px */
|
||||
}
|
||||
|
||||
%redundancy-zone header dl {
|
||||
@extend %horizontal-kv-list;
|
||||
@extend %pill-500;
|
||||
}
|
||||
%redundancy-zone header dt {
|
||||
@extend %visually-unhidden;
|
||||
}
|
||||
%redundancy-zone header dl:not(.warning) {
|
||||
background-color: rgb(var(--tone-gray-100));
|
||||
}
|
||||
%redundancy-zone header dl.warning {
|
||||
background-color: rgb(var(--tone-orange-100));
|
||||
color: rgb(var(--tone-orange-800));
|
||||
}
|
||||
%redundancy-zone header dl.warning::before {
|
||||
--icon-name: icon-alert-circle;
|
||||
--icon-size: icon-000;
|
||||
margin-right: 0.312rem; /* 5px */
|
||||
content: '';
|
||||
}
|
||||
%redundancy-zone header dt::after {
|
||||
content: ':';
|
||||
display: inline-block;
|
||||
vertical-align: revert;
|
||||
background-color: var(--transparent);
|
||||
}
|
||||
|
|
@ -47,6 +47,9 @@ as |source|>
|
|||
|
||||
{{! redirect if we aren't on a URL with dc information }}
|
||||
{{#if (eq route.currentName 'index')}}
|
||||
{{! until we get to the dc route we don't know any permissions }}
|
||||
{{! as we don't know the dc, any inital permission based }}
|
||||
{{! redirects are in the dc.show route}}
|
||||
{{did-insert (route-action 'replaceWith' 'dc.show'
|
||||
(hash
|
||||
dc=(env 'CONSUL_DATACENTER_LOCAL')
|
||||
|
|
|
@ -9,7 +9,8 @@ as |route|>
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<BlockSlot @name="nav">
|
||||
{{#if false}}
|
||||
<TabNav @items={{
|
||||
compact
|
||||
(array
|
||||
|
@ -18,12 +19,14 @@ as |route|>
|
|||
href=(href-to "dc.show.serverstatus")
|
||||
selected=(is-href "dc.show.serverstatus")
|
||||
)
|
||||
(if false
|
||||
(hash
|
||||
label=(compute (fn route.t 'health.title'))
|
||||
href=(href-to 'dc.show.health')
|
||||
selected=(is-href 'dc.show.health')
|
||||
label=(compute (fn route.t 'cataloghealth.title'))
|
||||
href=(href-to 'dc.show.cataloghealth')
|
||||
selected=(is-href 'dc.show.cataloghealth')
|
||||
)
|
||||
(if (and (can 'read license') (not (is 'hcp')))
|
||||
'')
|
||||
(if (can 'read license')
|
||||
(hash
|
||||
label=(compute (fn route.t 'license.title'))
|
||||
href=(href-to 'dc.show.license')
|
||||
|
@ -32,6 +35,15 @@ as |route|>
|
|||
)
|
||||
'')
|
||||
}}/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<Outlet
|
||||
@name={{routeName}}
|
||||
@model={{route.model}}
|
||||
as |o|>
|
||||
{{outlet}}
|
||||
</Outlet>
|
||||
</BlockSlot>
|
||||
|
||||
</AppView>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<Route
|
||||
@name={{routeName}}
|
||||
as |route|>
|
||||
{{did-insert (route-action 'replaceWith' (if (can 'access overview') 'dc.show.serverstatus' 'dc.services.index'))}}
|
||||
</Route>
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
<Route
|
||||
@name={{routeName}}
|
||||
as |route|>
|
||||
<DataLoader
|
||||
@src={{
|
||||
uri '/${partition}/${nspace}/${dc}/datacenter'
|
||||
(hash
|
||||
partition=route.params.partition
|
||||
nspace=route.params.nspace
|
||||
dc=route.params.dc
|
||||
)
|
||||
}}
|
||||
as |loader|>
|
||||
|
||||
{{#let
|
||||
loader.data
|
||||
as |item|}}
|
||||
<BlockSlot @name="error">
|
||||
<ErrorState
|
||||
@error={{loader.error}}
|
||||
@login={{route.model.app.login.open}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
|
||||
<BlockSlot @name="disconnected" as |after|>
|
||||
{{#if (eq loader.error.status "404")}}
|
||||
<Notice
|
||||
{{notification
|
||||
sticky=true
|
||||
}}
|
||||
class="notification-update"
|
||||
@type="warning"
|
||||
as |notice|>
|
||||
<notice.Header>
|
||||
<strong>Warning!</strong>
|
||||
</notice.Header>
|
||||
<notice.Body>
|
||||
<p>
|
||||
This service has been deregistered and no longer exists in the catalog.
|
||||
</p>
|
||||
</notice.Body>
|
||||
</Notice>
|
||||
{{else if (eq loader.error.status "403")}}
|
||||
<Notice
|
||||
{{notification
|
||||
sticky=true
|
||||
}}
|
||||
class="notification-update"
|
||||
@type="error"
|
||||
as |notice|>
|
||||
<notice.Header>
|
||||
<strong>Error!</strong>
|
||||
</notice.Header>
|
||||
<notice.Body>
|
||||
<p>
|
||||
You no longer have access to this service
|
||||
</p>
|
||||
</notice.Body>
|
||||
</Notice>
|
||||
{{else}}
|
||||
<Notice
|
||||
{{notification
|
||||
sticky=true
|
||||
}}
|
||||
class="notification-update"
|
||||
@type="warning"
|
||||
as |notice|>
|
||||
<notice.Header>
|
||||
<strong>Warning!</strong>
|
||||
</notice.Header>
|
||||
<notice.Body>
|
||||
<p>
|
||||
An error was returned whilst loading this data, refresh to try again.
|
||||
</p>
|
||||
</notice.Body>
|
||||
</Notice>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
|
||||
<BlockSlot @name="loaded">
|
||||
<div class="tab-section">
|
||||
|
||||
<section
|
||||
class={{class-map
|
||||
'server-failure-tolerance'
|
||||
}}
|
||||
>
|
||||
|
||||
<header>
|
||||
<h2>
|
||||
{{compute (fn route.t 'tolerance.header')}}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<section
|
||||
class={{class-map
|
||||
(array 'immediate-tolerance')
|
||||
}}
|
||||
>
|
||||
<header>
|
||||
<h3>
|
||||
{{compute (fn route.t 'tolerance.immediate.header')}}
|
||||
</h3>
|
||||
</header>
|
||||
<dl
|
||||
class={{class-map
|
||||
(array 'warning' (and
|
||||
(eq item.FailureTolerance 0)
|
||||
(eq item.OptimisticFailureTolerance 0)
|
||||
))
|
||||
}}
|
||||
>
|
||||
<dt>
|
||||
{{compute (fn route.t 'tolerance.immediate.body')}}
|
||||
</dt>
|
||||
<dd>
|
||||
{{item.FailureTolerance}}
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class={{class-map
|
||||
(array 'optimistic-tolerance')
|
||||
}}
|
||||
>
|
||||
<header>
|
||||
<h3>
|
||||
{{compute (fn route.t 'tolerance.optimistic.header')}}
|
||||
{{#if (not (can 'read zones'))}}
|
||||
<em>
|
||||
{{t 'common.ui.enterprisefeature'}}
|
||||
</em>
|
||||
{{/if}}
|
||||
<span
|
||||
{{tooltip 'With > 30 seconds between server failures, Consul can restore the Immediate Fault Tolerance by replacing failed active voters with healthy back-up voters when using redundancy zones.'}}
|
||||
>
|
||||
</span>
|
||||
</h3>
|
||||
</header>
|
||||
<dl
|
||||
class={{class-map
|
||||
(array 'warning' (eq item.OptimisticFailureTolerance 0))
|
||||
}}
|
||||
>
|
||||
<dt>
|
||||
{{compute (fn route.t 'tolerance.optimistic.body')}}
|
||||
</dt>
|
||||
<dd>
|
||||
{{item.OptimisticFailureTolerance}}
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
{{#if (gt item.RedundancyZones.length 0)}}
|
||||
<section
|
||||
class={{class-map
|
||||
'redundancy-zones'
|
||||
}}
|
||||
>
|
||||
<header>
|
||||
<h2>
|
||||
{{pluralize (t 'common.consul.redundancyzone')}}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
{{#each item.RedundancyZones as |item|}}
|
||||
{{#if (gt item.Servers.length 0) }}
|
||||
<section>
|
||||
<header>
|
||||
<h3>
|
||||
{{item.Name}}
|
||||
</h3>
|
||||
<dl
|
||||
class={{class-map
|
||||
(array 'warning' (eq item.FailureTolerance 0))
|
||||
}}
|
||||
>
|
||||
<dt
|
||||
>{{t 'common.consul.failuretolerance'}}</dt>
|
||||
<dd>{{item.FailureTolerance}}</dd>
|
||||
</dl>
|
||||
</header>
|
||||
<Consul::Server::List
|
||||
@items={{item.Servers}}
|
||||
/>
|
||||
</section>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
{{#if (gt item.Default.Servers.length 0)}}
|
||||
<section>
|
||||
<header>
|
||||
<h3>
|
||||
{{compute (fn route.t 'unassigned')}}
|
||||
</h3>
|
||||
</header>
|
||||
<Consul::Server::List
|
||||
@items={{item.Default.Servers}}
|
||||
/>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
</section>
|
||||
{{else}}
|
||||
<section>
|
||||
<header>
|
||||
<h2>
|
||||
{{compute (fn route.t 'servers')}}
|
||||
</h2>
|
||||
</header>
|
||||
<Consul::Server::List
|
||||
@items={{item.Default.Servers}}
|
||||
/>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
{{#if (gt item.ReadReplicas.length 0)}}
|
||||
<section>
|
||||
<header>
|
||||
<h2>
|
||||
{{pluralize (t 'common.consul.readreplica')}}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<Consul::Server::List
|
||||
@items={{item.ReadReplicas}}
|
||||
/>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</BlockSlot>
|
||||
{{/let}}
|
||||
</DataLoader>
|
||||
</Route>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
${[0].map(_ => {
|
||||
const servers = range(env('CONSUL_SERVER_COUNT', 3)).map(_ => fake.random.uuid());
|
||||
const zones = range(env('CONSUL_ZONE_COUNT', 3)).map(_ => fake.hacker.noun());
|
||||
const servers = range(env('CONSUL_SERVER_COUNT', 15)).map(_ => fake.random.uuid());
|
||||
const failureTolerance = Math.ceil(servers.length / 2);
|
||||
const optimisticTolerance = failureTolerance; // <== same for now
|
||||
const optimisticTolerance = 0;
|
||||
const leader = fake.random.number({min: 0, max: servers.length - 1});
|
||||
return `
|
||||
{
|
||||
|
@ -18,10 +19,10 @@ ${[0].map(_ => {
|
|||
"LastContact": "0s",
|
||||
"LastTerm": 2,
|
||||
"LastIndex": 91,
|
||||
"Healthy": true,
|
||||
"Healthy": ${fake.random.boolean()},
|
||||
"StableSince": "2022-02-02T11:59:01.0708146Z",
|
||||
"ReadReplica": false,
|
||||
"Status": "${i === leader ? `leader` : `voter`}",
|
||||
"Status": "${i === leader ? `leader` : fake.helpers.randomize(['non-voter', 'voter', 'staging'])}",
|
||||
"Meta": {
|
||||
"consul-network-segment": ""
|
||||
},
|
||||
|
@ -30,8 +31,26 @@ ${[0].map(_ => {
|
|||
`)}},
|
||||
"Leader": "${servers[leader]}",
|
||||
"Voters": [
|
||||
${servers.map(item => `"${item}"`)}
|
||||
],
|
||||
${ env('CONSUL_ZONES_ENABLE', false) ? `
|
||||
"RedundancyZones": {${zones.map((item, i) => `
|
||||
"${item}": {
|
||||
"Servers": [
|
||||
${servers.map(item => `"${item}"`)}
|
||||
],
|
||||
"Voters": [
|
||||
${servers.map(item => `"${item}"`)}
|
||||
],
|
||||
"FailureTolerance": ${i}
|
||||
}
|
||||
`)}
|
||||
},
|
||||
"ReadReplicas": [
|
||||
${servers.map(item => `"${item}"`)}
|
||||
]
|
||||
],
|
||||
` : ``}
|
||||
"Upgrade": {}
|
||||
}
|
||||
`;
|
||||
})}
|
||||
|
|
|
@ -52,6 +52,9 @@ module('Unit | Ability | *', function(hooks) {
|
|||
// TODO: We currently hardcode KVs to always be true
|
||||
assert.equal(true, ability[`can${perm}`], `Expected ${item}.can${perm} to be true`);
|
||||
return;
|
||||
case 'zone':
|
||||
// Zone permissions depend on NSPACES_ENABLED
|
||||
return;
|
||||
}
|
||||
assert.equal(
|
||||
bool,
|
||||
|
|
|
@ -14,6 +14,7 @@ ui:
|
|||
name: Name
|
||||
creation: Creation
|
||||
maxttl: Max TTL
|
||||
enterprisefeature: Enterprise feature
|
||||
consul:
|
||||
name: Name
|
||||
passing: Passing
|
||||
|
@ -41,6 +42,9 @@ consul:
|
|||
destinationname: Destination Name
|
||||
sourcename: Source Name
|
||||
displayname: Display Name
|
||||
failuretolerance: Fault tolerance
|
||||
readreplica: Read replica
|
||||
redundancyzone: Redundancy zone
|
||||
search:
|
||||
search: Search
|
||||
searchproperty: Search Across
|
||||
|
|
|
@ -3,10 +3,20 @@ dc:
|
|||
title: Cluster Overview
|
||||
serverstatus:
|
||||
title: Server status
|
||||
health:
|
||||
unassigned: Unassigned Zones
|
||||
tolerance:
|
||||
header: Server fault tolerance
|
||||
immediate:
|
||||
header: Immediate
|
||||
body: the number of healthy active voting servers that can fail at once without causing an outage
|
||||
optimistic:
|
||||
header: Optimistic
|
||||
body: the number of healthy active and back-up voting servers that can fail gradually without causing an outage
|
||||
cataloghealth:
|
||||
title: Health
|
||||
license:
|
||||
title: License
|
||||
|
||||
nodes:
|
||||
show:
|
||||
healthchecks:
|
||||
|
|
|
@ -13,18 +13,17 @@
|
|||
show: {
|
||||
_options: {
|
||||
path: '/overview',
|
||||
redirect: './serverstatus',
|
||||
abilities: ['access overview']
|
||||
},
|
||||
serverstatus: {
|
||||
_options: {
|
||||
path: '/server-status',
|
||||
abilities: ['access overview', 'read raft']
|
||||
abilities: ['access overview', 'read zones']
|
||||
},
|
||||
},
|
||||
health: {
|
||||
cataloghealth: {
|
||||
_options: {
|
||||
path: '/health',
|
||||
path: '/catalog-health',
|
||||
abilities: ['access overview']
|
||||
},
|
||||
},
|
||||
|
@ -417,6 +416,7 @@
|
|||
},
|
||||
index: {
|
||||
_options: { path: '/' },
|
||||
// root index redirects are currently dealt with in application.hbs
|
||||
},
|
||||
settings: {
|
||||
_options: {
|
||||
|
|
Loading…
Reference in New Issue