feat(xo-web/host/network): display and edit the IPv6 PIF field
This commit is contained in:
parent
b95b1622b1
commit
1becccffbc
@ -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-->
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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',
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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',
|
||||
|
||||
|
@ -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',
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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',
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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 })
|
||||
|
@ -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>
|
||||
|
||||
<SingleLineRow>
|
||||
<Col size={6}>{_('staticIpv6')}</Col>
|
||||
<Col size={6}>
|
||||
<input className='form-control' onChange={this.linkState('ipv6')} value={ipv6} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user