diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 4cfc851dc..8869820b7 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -30,5 +30,6 @@ - xo-server-netbox patch +- xo-web patch diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js index 019fdb08c..92fbc5d2d 100644 --- a/packages/xo-web/src/common/intl/messages.js +++ b/packages/xo-web/src/common/intl/messages.js @@ -2504,6 +2504,10 @@ const messages = { fieldsMissing: 'Some fields are missing', hostsNotSameNumberOfDisks: 'Hosts do not have the same number of disks', isTapdevsDisk: 'This is "tapdevs" disk', + licenseBoundUnknownXostor: 'License attached to an unknown XOSTOR', + licenseNotBoundXostor: 'No XOSTOR attached', + licenseExpiredXostorWarning: + 'The license {licenseId} has expired. You can still use the SR but cannot administrate it anymore.', networks: 'Networks', notXcpPool: 'Not an XCP-ng pool', noXostorFound: 'No XOSTOR found', diff --git a/packages/xo-web/src/xo-app/xoa/licenses/index.js b/packages/xo-web/src/xo-app/xoa/licenses/index.js index 738ac21db..95d3692ea 100644 --- a/packages/xo-web/src/xo-app/xoa/licenses/index.js +++ b/packages/xo-web/src/xo-app/xoa/licenses/index.js @@ -128,6 +128,22 @@ const LicenseManager = ({ item, userData }) => { } } + if (type === 'xostor') { + const { srId } = item + + if (srId === undefined) { + return _('licenseNotBoundXostor') + } + + const sr = userData.xostorSrs[srId] + return ( + + {sr === undefined ? _('licenseBoundUnknownXostor') : {renderXoItem(sr)}}{' '} + + + ) + } + console.warn('encountered unsupported license type') return null } @@ -174,11 +190,15 @@ const PRODUCTS_COLUMNS = [ // ----------------------------------------------------------------------------- @adminOnly -@connectStore({ - xosanSrs: createGetObjectsOfType('SR').filter([ - ({ SR_type }) => SR_type === 'xosan', // eslint-disable-line camelcase - ]), - xoaRegistration: state => state.xoaRegisterState, +@connectStore(() => { + const getSrs = createGetObjectsOfType('SR') + return { + xosanSrs: getSrs.filter([ + ({ SR_type }) => SR_type === 'xosan', // eslint-disable-line camelcase + ]), + xoaRegistration: state => state.xoaRegisterState, + xostorSrs: getSrs.filter([({ SR_type }) => SR_type === 'linstor']), + } }) @addSubscriptions(() => ({ plugins: subscribePlugins, @@ -363,7 +383,7 @@ export default class Licenses extends Component { return {_('statusLoading')} } - const { xoaRegistration, selfLicenses, xosanSrs } = this.props + const { xoaRegistration, selfLicenses, xosanSrs, xostorSrs } = this.props return ( @@ -390,6 +410,7 @@ export default class Licenses extends Component { data-registeredEmail={xoaRegistration.email} data-selfLicenses={selfLicenses} data-xosanSrs={xosanSrs} + data-xostorSrs={xostorSrs} stateUrlParam='s' /> diff --git a/packages/xo-web/src/xo-app/xoa/licenses/xostor.js b/packages/xo-web/src/xo-app/xoa/licenses/xostor.js index d403e907a..33e135d57 100644 --- a/packages/xo-web/src/xo-app/xoa/licenses/xostor.js +++ b/packages/xo-web/src/xo-app/xoa/licenses/xostor.js @@ -6,14 +6,15 @@ import Icon from 'icon' import React from 'react' import SelectLicense from 'select-license' import SortedTable from 'sorted-table' -import Tooltip from 'tooltip' import { bindLicense } from 'xo' import { connectStore } from 'utils' -import { createGetObjectsOfType } from 'selectors' +import { createGetObjectsOfType, createSelector } from 'selectors' import { groupBy } from 'lodash' import { injectState, provideState } from 'reaclette' import { Pool, Sr } from 'render-xo-item' +import BulkIcons from '../../../common/bulk-icons' + class XostorLicensesForm extends Component { state = { licenseId: 'none', @@ -24,40 +25,72 @@ class XostorLicensesForm extends Component { return bindLicense(this.state.licenseId, item.uuid).then(userData.updateLicenses) } + getAlerts = createSelector( + () => this.props.item, + () => this.props.userData, + (sr, userData) => { + const alerts = [] + const licenses = userData.licensesByXostorUuid[sr.id] + + // Xostor bound to multiple licenses + if (licenses?.length > 1) { + alerts.push({ + level: 'danger', + render: ( +

+ {_('xostorMultipleLicenses')} +
+ {licenses.map(license => license.id.slice(-4)).join(',')} +

+ ), + }) + } + + const license = licenses?.[0] + if (license?.expires < Date.now()) { + alerts.push({ + level: 'danger', + render: _('licenseExpiredXostorWarning', { licenseId: license?.id.slice(-4) }), + }) + } + return alerts + } + ) + render() { + const alerts = this.getAlerts() + if (alerts.length > 0) { + return + } + const { item, userData } = this.props const { licenseId } = this.state const licenses = userData.licensesByXostorUuid[item.id] - - // Xostor bound to multiple licenses - if (licenses?.length > 1) { - return ( -
- {licenses.map(license => license.id.slice(-4)).join(',')}{' '} - - - -
- ) - } - const license = licenses?.[0] + return license !== undefined ? ( - {license.id.slice(-4)} + {license?.id.slice(-4)} ) : ( -
- - - {_('bindLicense')} - - +
+ {license !== undefined && ( +
+ {_('licenseHasExpired')} +
+ )} +
+ + + {_('bindLicense')} + + +
) } }