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” > 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 ### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed” > Users must be able to say: “I had this issue, happy to know it's fixed”
@ -28,5 +30,6 @@
<!--packages-start--> <!--packages-start-->
- xo-server minor - xo-server minor
- xo-web minor
<!--packages-end--> <!--packages-end-->

View File

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

View File

@ -1596,9 +1596,6 @@ export default {
// Original text: "Invalid parameters" // Original text: "Invalid parameters"
configIpErrorTitle: 'Paramètres invalides', 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" // Original text: "Static IP address"
staticIp: 'Adresse IP statique', staticIp: 'Adresse IP statique',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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