feat(xo-server,xo-web/VIF): set any allowed IP as an admin (#5367)
Fixes #2535 See #1872 See #5358
This commit is contained in:
parent
1027659f34
commit
ee7217c7c9
@ -11,6 +11,7 @@
|
||||
- [Licensing] Allow Free and Starter users to copy VMs and create a VM from snapshot on the same pool [#4890](https://github.com/vatesfr/xen-orchestra/issues/4890) (PR [5333](https://github.com/vatesfr/xen-orchestra/pull/5333))
|
||||
- [SR] Use SR type `zfs` instead of `file` for ZFS storage repositories (PR [5302](https://github.com/vatesfr/xen-orchestra/pull/5330))
|
||||
- [Dashboard/Health] List VMs with missing or outdated guest tools (PR [#5376](https://github.com/vatesfr/xen-orchestra/pull/5376))
|
||||
- [VIF] Ability for admins to set any allowed IPs, including IPv6 and IPs that are not in an IP pool [#2535](https://github.com/vatesfr/xen-orchestra/issues/2535) [#1872](https://github.com/vatesfr/xen-orchestra/issues/1872) (PR [#5367](https://github.com/vatesfr/xen-orchestra/pull/5367))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
@ -76,15 +76,24 @@ export async function set({
|
||||
resourceSet,
|
||||
txChecksumming,
|
||||
}) {
|
||||
// - If allowed IPs were explicitly passed: use them
|
||||
// - Else if the network is changing: remove the existing allowed IPs
|
||||
// - Else: use the old IPs
|
||||
const newIpv4Addresses =
|
||||
allowedIpv4Addresses ??
|
||||
(network === undefined || network.id === vif.$network
|
||||
? vif.allowedIpv4Addresses
|
||||
: [])
|
||||
const newIpv6Addresses =
|
||||
allowedIpv6Addresses ??
|
||||
(network === undefined || network.id === vif.$network
|
||||
? vif.allowedIpv6Addresses
|
||||
: [])
|
||||
|
||||
const oldIpAddresses = vif.allowedIpv4Addresses.concat(
|
||||
vif.allowedIpv6Addresses
|
||||
)
|
||||
const newIpAddresses = []
|
||||
{
|
||||
const { push } = newIpAddresses
|
||||
push.apply(newIpAddresses, allowedIpv4Addresses || vif.allowedIpv4Addresses)
|
||||
push.apply(newIpAddresses, allowedIpv6Addresses || vif.allowedIpv6Addresses)
|
||||
}
|
||||
const newIpAddresses = newIpv4Addresses.concat(newIpv6Addresses)
|
||||
|
||||
if (lockingMode !== undefined) {
|
||||
await this.checkPermissions(this.user.id, [
|
||||
@ -118,7 +127,8 @@ export async function set({
|
||||
const newVif = await xapi.createVif(vm.$id, network.$id, {
|
||||
mac,
|
||||
currently_attached: attached,
|
||||
ipv4_allowed: newIpAddresses,
|
||||
ipv4_allowed: newIpv4Addresses,
|
||||
ipv6_allowed: newIpv6Addresses,
|
||||
locking_mode: lockingMode ?? vif.lockingMode,
|
||||
qos_algorithm_type: rateLimit != null ? 'ratelimit' : undefined,
|
||||
qos_algorithm_params:
|
||||
|
@ -1182,6 +1182,11 @@ const messages = {
|
||||
vifIpAddresses: 'IP addresses',
|
||||
vifMacAutoGenerate: 'Auto-generated if empty',
|
||||
vifAllowedIps: 'Allowed IPs',
|
||||
selectIpFromIpPool: 'Select an IP',
|
||||
enterIp: 'Enter an IP',
|
||||
addAllowedIp: 'Add allowed IP',
|
||||
addIpError: 'Error while adding allowed IP',
|
||||
invalidIp: 'Invalid IP',
|
||||
vifNoIps: 'No IPs',
|
||||
vifLockingModeDisabled: 'VIF locking mode is disabled',
|
||||
vifLockingModeUnlocked: 'VIF locking mode is unlocked',
|
||||
|
@ -118,27 +118,18 @@ class VifNetwork extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
@connectStore({
|
||||
isAdmin,
|
||||
})
|
||||
@addSubscriptions({
|
||||
ipPools: subscribeIpPools,
|
||||
resourceSets: subscribeResourceSets,
|
||||
})
|
||||
@injectIntl
|
||||
class VifAllowedIps extends BaseComponent {
|
||||
_saveIp = (ipIndex, newIp) => {
|
||||
if (!isIp(newIp.id)) {
|
||||
throw new Error('Not a valid IP')
|
||||
}
|
||||
const vif = this.props.item
|
||||
const { allowedIpv4Addresses, allowedIpv6Addresses } = vif
|
||||
if (isIpV4(newIp.id)) {
|
||||
allowedIpv4Addresses[ipIndex] = newIp.id
|
||||
} else {
|
||||
allowedIpv6Addresses[ipIndex - allowedIpv4Addresses.length] = newIp.id
|
||||
}
|
||||
setVif(vif, { allowedIpv4Addresses, allowedIpv6Addresses })
|
||||
}
|
||||
_addIp = ip => {
|
||||
this._toggleNewIp()
|
||||
if (!isIp(ip.id)) {
|
||||
error(_('addIpError'), _('invalidIp'))
|
||||
return
|
||||
}
|
||||
const vif = this.props.item
|
||||
@ -148,7 +139,11 @@ class VifAllowedIps extends BaseComponent {
|
||||
} else {
|
||||
allowedIpv6Addresses = [...allowedIpv6Addresses, ip.id]
|
||||
}
|
||||
setVif(vif, { allowedIpv4Addresses, allowedIpv6Addresses })
|
||||
return setVif(vif, { allowedIpv4Addresses, allowedIpv6Addresses }).catch(
|
||||
err => {
|
||||
error(_('addIpError'), err.message)
|
||||
}
|
||||
)
|
||||
}
|
||||
_deleteIp = ipIndex => {
|
||||
const vif = this.props.item
|
||||
@ -200,12 +195,39 @@ class VifAllowedIps extends BaseComponent {
|
||||
find(ipPool.networks, ipPoolNetwork => ipPoolNetwork === vifNetworkId)
|
||||
)
|
||||
|
||||
_toggleNewIp = () =>
|
||||
this.setState({ showNewIpForm: !this.state.showNewIpForm })
|
||||
_hideIpInputs = () =>
|
||||
this.setState({
|
||||
showIpSelector: false,
|
||||
showIpInput: false,
|
||||
})
|
||||
|
||||
_onKeyDown = async event => {
|
||||
const { key, target } = event
|
||||
|
||||
if (key === 'Enter') {
|
||||
if (target.value != null) {
|
||||
target.disabled = true
|
||||
await this._addIp({ id: target.value })
|
||||
target.disabled = false
|
||||
target.value = ''
|
||||
}
|
||||
} else if (key === 'Escape' || key === 'Esc') {
|
||||
this._hideIpInputs()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { showNewIpForm } = this.state
|
||||
const { resourceSet, item: vif } = this.props
|
||||
const { showIpSelector, showIpInput } = this.state
|
||||
const {
|
||||
intl: { formatMessage },
|
||||
isAdmin,
|
||||
item: vif,
|
||||
resourceSet,
|
||||
} = this.props
|
||||
|
||||
if (!vif) {
|
||||
return null
|
||||
@ -231,18 +253,7 @@ class VifAllowedIps extends BaseComponent {
|
||||
) : (
|
||||
map(this._getIps(), (ip, ipIndex) => (
|
||||
<Row>
|
||||
<Col size={10}>
|
||||
<XoSelect
|
||||
containerPredicate={this._getIsNetworkAllowed()}
|
||||
onChange={newIp => this._saveIp(ipIndex, newIp)}
|
||||
predicate={this._getIpPredicate()}
|
||||
resourceSetId={resourceSet}
|
||||
value={ip}
|
||||
xoType={resourceSet ? 'resourceSetIp' : 'ip'}
|
||||
>
|
||||
{ip}
|
||||
</XoSelect>
|
||||
</Col>
|
||||
<Col size={10}>{ip}</Col>
|
||||
<Col size={1}>
|
||||
<ActionRowButton
|
||||
handler={this._deleteIp}
|
||||
@ -255,8 +266,8 @@ class VifAllowedIps extends BaseComponent {
|
||||
)}
|
||||
<Row>
|
||||
<Col size={10}>
|
||||
{showNewIpForm ? (
|
||||
<span onBlur={this._toggleNewIp}>
|
||||
{showIpSelector && (
|
||||
<span onBlur={this._hideIpInputs}>
|
||||
{resourceSet ? (
|
||||
<SelectResourceSetIp
|
||||
autoFocus
|
||||
@ -264,6 +275,7 @@ class VifAllowedIps extends BaseComponent {
|
||||
onChange={this._addIp}
|
||||
predicate={this._getIpPredicate()}
|
||||
resourceSetId={resourceSet}
|
||||
value={null}
|
||||
/>
|
||||
) : (
|
||||
<SelectIp
|
||||
@ -271,21 +283,43 @@ class VifAllowedIps extends BaseComponent {
|
||||
containerPredicate={this._getIsNetworkAllowed()}
|
||||
onChange={this._addIp}
|
||||
predicate={this._getIpPredicate()}
|
||||
value={null}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<ActionButton
|
||||
btnStyle='success'
|
||||
size='small'
|
||||
handler={this._toggleNewIp}
|
||||
icon='add'
|
||||
)}
|
||||
{showIpInput && (
|
||||
<input
|
||||
autoFocus
|
||||
onBlur={this._hideIpInputs}
|
||||
onKeyDown={this._onKeyDown}
|
||||
placeholder={formatMessage(messages.addAllowedIp)}
|
||||
/>
|
||||
)}{' '}
|
||||
{warningMessage !== undefined && (
|
||||
<Tooltip content={warningMessage}>
|
||||
<Icon icon='error' />
|
||||
</Tooltip>
|
||||
)}
|
||||
{!showIpSelector && !showIpInput && (
|
||||
<span>
|
||||
<ActionButton
|
||||
btnStyle='success'
|
||||
size='small'
|
||||
handler={this.toggleState('showIpSelector')}
|
||||
icon='add'
|
||||
tooltip={_('selectIpFromIpPool')}
|
||||
/>{' '}
|
||||
{isAdmin && (
|
||||
<ActionButton
|
||||
btnStyle='primary'
|
||||
size='small'
|
||||
handler={this.toggleState('showIpInput')}
|
||||
icon='edit'
|
||||
tooltip={_('enterIp')}
|
||||
/>
|
||||
)}{' '}
|
||||
{warningMessage !== undefined && (
|
||||
<Tooltip content={warningMessage}>
|
||||
<Icon icon='error' />
|
||||
</Tooltip>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
|
Loading…
Reference in New Issue
Block a user