feat(xo-web/host/network): display and edit the IPv6 PIF field

This commit is contained in:
mathieuRA 2023-12-26 10:10:07 +01:00 committed by Julien Fontanet
parent b95b1622b1
commit 1becccffbc
13 changed files with 55 additions and 42 deletions

View File

@ -7,6 +7,8 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [Host/Network/PIF] Display and ability to edit IPv6 field [#5400](https://github.com/vatesfr/xen-orchestra/issues/5400) (PR [#7218](https://github.com/vatesfr/xen-orchestra/pull/7218))
### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed”
@ -28,5 +30,6 @@
<!--packages-start-->
- xo-server minor
- xo-web minor
<!--packages-end-->

View File

@ -1541,9 +1541,6 @@ export default {
// Original text: 'Invalid parameters'
configIpErrorTitle: undefined,
// Original text: 'IP address and netmask required'
configIpErrorMessage: undefined,
// Original text: 'Static IP address'
staticIp: undefined,

View File

@ -1596,9 +1596,6 @@ export default {
// Original text: "Invalid parameters"
configIpErrorTitle: 'Paramètres invalides',
// Original text: "IP address and netmask required"
configIpErrorMessage: 'Adresse IP et masque de réseau requis',
// Original text: "Static IP address"
staticIp: 'Adresse IP statique',

View File

@ -1295,9 +1295,6 @@ export default {
// Original text: 'Invalid parameters'
configIpErrorTitle: undefined,
// Original text: 'IP address and netmask required'
configIpErrorMessage: undefined,
// Original text: 'Static IP address'
staticIp: undefined,

View File

@ -1491,9 +1491,6 @@ export default {
// Original text: "Invalid parameters"
configIpErrorTitle: 'Invalid parameters',
// Original text: "IP address and netmask required"
configIpErrorMessage: 'IP cím and netmask required',
// Original text: "Static IP address"
staticIp: 'Static IP cím',

View File

@ -2387,9 +2387,6 @@ export default {
// Original text: 'Invalid parameters'
configIpErrorTitle: 'Parametri non validi',
// Original text: 'IP address and netmask required'
configIpErrorMessage: 'Indirizzo IP e maschera di rete richiesti',
// Original text: 'Static IP address'
staticIp: 'Indirizzo IP statico',

View File

@ -1298,9 +1298,6 @@ export default {
// Original text: 'Invalid parameters'
configIpErrorTitle: undefined,
// Original text: 'IP address and netmask required'
configIpErrorMessage: undefined,
// Original text: 'Static IP address'
staticIp: undefined,

View File

@ -1296,9 +1296,6 @@ export default {
// Original text: 'Invalid parameters'
configIpErrorTitle: undefined,
// Original text: 'IP address and netmask required'
configIpErrorMessage: undefined,
// Original text: 'Static IP address'
staticIp: undefined,

View File

@ -1542,9 +1542,6 @@ export default {
// Original text: 'Invalid parameters'
configIpErrorTitle: undefined,
// Original text: 'IP address and netmask required'
configIpErrorMessage: undefined,
// Original text: 'Static IP address'
staticIp: undefined,

View File

@ -1990,9 +1990,6 @@ export default {
// Original text: "Invalid parameters"
configIpErrorTitle: 'Geçersiz parametre',
// Original text: "IP address and netmask required"
configIpErrorMessage: 'IP adresi ve ağ maskesi gerekli',
// Original text: "Static IP address"
staticIp: 'Statik IP adresi',

View File

@ -1072,11 +1072,13 @@ const messages = {
defaultLockingMode: 'Default locking mode',
pifConfigureIp: 'Configure IP address',
configIpErrorTitle: 'Invalid parameters',
configIpErrorMessage: 'IP address and netmask required',
staticIp: 'Static IP address',
staticIpv6: 'Static IPv6 address',
netmask: 'Netmask',
dns: 'DNS',
gateway: 'Gateway',
ipRequired: 'An IP address is required',
netmaskRequired: 'Netmask required',
// ----- Host storage tabs -----
addSrDeviceButton: 'Add a storage',
srType: 'Type',

View File

@ -2244,11 +2244,13 @@ export const deletePifs = pifs =>
body: _('deletePifsConfirm', { nPifs: pifs.length }),
}).then(() => Promise.all(map(pifs, pif => _call('pif.delete', { pif: resolveId(pif) }))), noop)
export const reconfigurePifIp = (pif, { mode, ip, netmask, gateway, dns }) =>
export const reconfigurePifIp = (pif, { mode, ip, ipv6, ipv6Mode, netmask, gateway, dns }) =>
_call('pif.reconfigureIp', {
pif: resolveId(pif),
mode,
ip,
ipv6,
ipv6Mode,
netmask,
gateway,
dns,
@ -2256,6 +2258,8 @@ export const reconfigurePifIp = (pif, { mode, ip, netmask, gateway, dns }) =>
export const getIpv4ConfigModes = () => _call('pif.getIpv4ConfigurationModes')
export const getIpv6ConfigModes = () => _call('pif.getIpv6ConfigurationModes')
export const editPif = (pif, { vlan }) => _call('pif.editPif', { pif: resolveId(pif), vlan })
export const scanHostPifs = hostId => _call('host.scanPifs', { host: hostId })

View File

@ -26,6 +26,7 @@ import {
editNetwork,
editPif,
getIpv4ConfigModes,
getIpv6ConfigModes,
reconfigurePifIp,
scanHostPifs,
} from 'xo'
@ -41,7 +42,10 @@ class ConfigureIpModal extends Component {
const { pif } = props
if (pif) {
this.state = pick(pif, ['ip', 'netmask', 'dns', 'gateway'])
this.state = {
...pick(pif, ['ip', 'netmask', 'dns', 'gateway']),
ipv6: pif.ipv6?.[0],
}
}
}
@ -50,7 +54,7 @@ class ConfigureIpModal extends Component {
}
render() {
const { ip, netmask, dns, gateway } = this.state
const { ip, ipv6, netmask, dns, gateway } = this.state
return (
<div>
@ -61,6 +65,13 @@ class ConfigureIpModal extends Component {
</Col>
</SingleLineRow>
&nbsp;
<SingleLineRow>
<Col size={6}>{_('staticIpv6')}</Col>
<Col size={6}>
<input className='form-control' onChange={this.linkState('ipv6')} value={ipv6} />
</Col>
</SingleLineRow>
&nbsp;
<SingleLineRow>
<Col size={6}>{_('netmask')}</Col>
<Col size={6}>
@ -111,14 +122,19 @@ const reconfigureIp = (pif, mode) => {
title: _('pifConfigureIp'),
body: <ConfigureIpModal pif={pif} />,
}).then(params => {
if (!params.ip || !params.netmask) {
error(_('configIpErrorTitle'), _('configIpErrorMessage'))
return
if (params.ip === undefined && params.ipv6 === undefined) {
return error(_('configIpErrorTitle'), _('ipRequired'))
}
if (params.ip !== undefined && params.netmask === undefined) {
return error(_('configIpErrorTitle'), _('netmaskRequired'))
}
if (params.ipv6 !== undefined) {
params.ipv6Mode = mode
}
return reconfigurePifIp(pif, { mode, ...params })
}, noop)
}
return reconfigurePifIp(pif, { mode })
return reconfigurePifIp(pif, { [pif.primaryAddressType === 'IPv6' ? 'ipv6Mode' : 'mode']: mode })
}
class PifItemIp extends Component {
@ -126,7 +142,7 @@ class PifItemIp extends Component {
render() {
const { pif } = this.props
const pifIp = pif.ip
const pifIp = pif.primaryAddressType === 'IPv6' ? pif.ipv6?.[0] : pif.ip
return (
<div>
{pifIp}{' '}
@ -143,26 +159,41 @@ class PifItemIp extends Component {
class PifItemMode extends Component {
state = { configModes: [] }
componentDidMount() {
getIpv4ConfigModes().then(configModes => this.setState({ configModes }))
async componentDidMount() {
const [ipv4ConfigModes, ipv6ConfigModes] = await Promise.all([getIpv4ConfigModes(), getIpv6ConfigModes()])
this.setState({ ipv4ConfigModes, ipv6ConfigModes })
}
_configIp = mode => mode != null && reconfigureIp(this.props.pif, mode.value)
_isIpv6 = createSelector(
() => this.props.pif.primaryAddressType,
primaryAddressType => primaryAddressType === 'IPv6'
)
_getOptions = createSelector(
() => this.state.configModes,
configModes => configModes.map(mode => ({ label: mode, value: mode }))
this._isIpv6,
() => this.state.ipv4ConfigModes,
() => this.state.ipv6ConfigModes,
(isIpv6, ipv4ConfigModes, ipv6ConfigModes) =>
(isIpv6 ? ipv6ConfigModes : ipv4ConfigModes).map(mode => ({ label: mode, value: mode }))
)
_getValue = createSelector(
this._isIpv6,
() => this.props.pif.mode,
mode => ({ label: mode, value: mode })
() => this.props.pif.ipv6Mode,
(isIpv6, mode, ipv6Mode) => {
mode = isIpv6 ? ipv6Mode : mode
return { label: mode, value: mode }
}
)
render() {
const isIpv6 = this._isIpv6()
return (
<Select onChange={this._configIp} options={this._getOptions()} value={this._getValue()}>
{this.props.pif.mode}
{this.props.pif[isIpv6 ? 'ipv6Mode' : 'mode']}
</Select>
)
}