diff --git a/packages/xo-web/src/xo-app/dashboard/health/unhealthyVdis.js b/packages/xo-web/src/xo-app/dashboard/health/unhealthyVdis.js
new file mode 100644
index 00000000000..3346143b852
--- /dev/null
+++ b/packages/xo-web/src/xo-app/dashboard/health/unhealthyVdis.js
@@ -0,0 +1,87 @@
+import _ from 'intl'
+import addSubscriptions from 'add-subscriptions'
+import decorate from 'apply-decorators'
+import Icon from 'icon'
+import React from 'react'
+import SingleLineRow from 'single-line-row'
+import SortedTable from 'sorted-table'
+import Tooltip from 'tooltip'
+import { Card, CardHeader, CardBlock } from 'card'
+import { Col, Row } from 'grid'
+import { injectState, provideState } from 'reaclette'
+import { map, size } from 'lodash'
+import { Sr, Vdi } from 'render-xo-item'
+import { subscribeSrsUnhealthyVdiChainsLength, VDIS_TO_COALESCE_LIMIT } from 'xo'
+
+const COLUMNS = [
+ {
+ itemRenderer: (srId, { unhealthyVdiChainsLengthBySr }) => (
+
+ {' '}
+ {size(unhealthyVdiChainsLengthBySr[srId]) >= VDIS_TO_COALESCE_LIMIT && (
+
+
+
+
+
+ )}
+
+ ),
+ name: _('sr'),
+ sortCriteria: 'name_label',
+ },
+ {
+ itemRenderer: (srId, { unhealthyVdiChainsLengthBySr }) => (
+
+ {map(unhealthyVdiChainsLengthBySr[srId], (chainLength, vdiId) => (
+
+
+
+
+
+ {_('length', { length: chainLength })}
+
+
+ ))}
+
+ ),
+ name: _('vdisToCoalesce'),
+ },
+]
+
+const UnhealthyVdis = decorate([
+ addSubscriptions({
+ unhealthyVdiChainsLengthBySr: subscribeSrsUnhealthyVdiChainsLength,
+ }),
+ provideState({
+ computed: {
+ srIds: (state, { unhealthyVdiChainsLengthBySr = {} }) => Object.keys(unhealthyVdiChainsLengthBySr),
+ },
+ }),
+ injectState,
+ ({ state: { srIds }, unhealthyVdiChainsLengthBySr }) => (
+
+
+
+
+ {_('vdisToCoalesce')}
+
+
+
+
+
+
+
+
+
+
+
+ ),
+])
+
+export default UnhealthyVdis
diff --git a/packages/xo-web/src/xo-app/menu/index.js b/packages/xo-web/src/xo-app/menu/index.js
index a6e27aabe04..e894fdef804 100644
--- a/packages/xo-web/src/xo-app/menu/index.js
+++ b/packages/xo-web/src/xo-app/menu/index.js
@@ -18,6 +18,8 @@ import {
subscribeProxies,
subscribeProxiesApplianceUpdaterState,
subscribeResourceSets,
+ subscribeSrsUnhealthyVdiChainsLength,
+ VDIS_TO_COALESCE_LIMIT,
} from 'xo'
import {
createFilter,
@@ -29,7 +31,7 @@ import {
getXoaState,
isAdmin,
} from 'selectors'
-import { every, forEach, identity, isEmpty, isEqual, map, pick, some } from 'lodash'
+import { every, forEach, identity, isEmpty, isEqual, map, pick, size, some } from 'lodash'
import styles from './index.css'
@@ -67,6 +69,7 @@ const returnTrue = () => true
cb(map(proxies, 'id').sort())
}),
resourceSets: subscribeResourceSets,
+ unhealthyVdiChainsLength: subscribeSrsUnhealthyVdiChainsLength,
})
@injectState
export default class Menu extends Component {
@@ -135,6 +138,12 @@ export default class Menu extends Component {
missingPatches => some(missingPatches, _ => _)
)
+ _hasUnhealthyVdis = createSelector(
+ () => this.state.unhealthyVdiChainsLength,
+ unhealthyVdiChainsLength =>
+ some(unhealthyVdiChainsLength, vdiChainsLength => size(vdiChainsLength) >= VDIS_TO_COALESCE_LIMIT)
+ )
+
_toggleCollapsed = event => {
event.preventDefault()
this._removeListener()
@@ -211,6 +220,14 @@ export default class Menu extends Component {
) : null
+ const unhealthyVdisWarning = this._hasUnhealthyVdis() ? (
+
+
+
+
+
+ ) : null
+
/* eslint-disable object-property-newline */
const items = [
{
@@ -247,6 +264,7 @@ export default class Menu extends Component {
to: '/dashboard/overview',
icon: 'menu-dashboard',
label: 'dashboardPage',
+ extra: [unhealthyVdisWarning],
subMenu: [
{
to: '/dashboard/overview',
@@ -267,6 +285,7 @@ export default class Menu extends Component {
to: '/dashboard/health',
icon: 'menu-dashboard-health',
label: 'overviewHealthDashboardPage',
+ extra: [unhealthyVdisWarning],
},
],
},