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:
Pierre Donias 2020-11-20 10:35:23 +01:00 committed by GitHub
parent 1027659f34
commit ee7217c7c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 51 deletions

View File

@ -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

View File

@ -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:

View File

@ -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',

View File

@ -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>