Compare commits

..

8 Commits

Author SHA1 Message Date
Florent Beauchamp
4b9e11e0ff fix(backups/mirror): use stable uuid for chaining 2024-02-12 16:52:56 +00:00
Smultar
e2d83324ac chore: add name and version to root package.json (#7372)
Fixes #7371
2024-02-12 16:59:50 +01:00
Julien Fontanet
7cea445c21 fix(xo-web/remotes): don't merge all properties into url
Related to #7343

Introduced by fb1bf6a1e7
2024-02-12 14:51:04 +01:00
Julien Fontanet
b5d9d9a9e1 fix(xo-server-audit): ignore tag.getAllConfigured
Introduced by 25e270edb4
2024-02-12 10:58:06 +01:00
Julien Fontanet
3a4e9b8f8e chore(xo-web/config): remove unused computeds
Introduced by 01302d7a60
2024-02-12 10:55:58 +01:00
Julien Fontanet
92efd28b33 fix(xo-web/config): sort backups from newest to oldest
Introduced by 01302d7a60
2024-02-12 10:55:26 +01:00
Julien Fontanet
a2c36c0832 feat(xo-server): add robots.txt
Fixes zammad#21489
2024-02-09 11:25:06 +01:00
Florent BEAUCHAMP
2eb49cfdf1 feat: release 5.91.2 (#7367) 2024-02-09 11:10:59 +01:00
13 changed files with 84 additions and 85 deletions

View File

@@ -218,16 +218,16 @@ export class IncrementalRemoteWriter extends MixinRemoteWriter(AbstractIncrement
})
transferSize += transferSizeOneDisk
if (isDifferencing) {
await chainVhd(handler, parentPath, handler, path)
}
// set the correct UUID in the VHD
await Disposable.use(openVhd(handler, path), async vhd => {
vhd.footer.uuid = packUuid(vdi.uuid)
await vhd.readBlockAllocationTable() // required by writeFooter()
await vhd.writeFooter()
})
if (isDifferencing) {
await chainVhd(handler, parentPath, handler, path)
}
},
{
concurrency: settings.diskPerVmConcurrency,

View File

@@ -1,6 +1,8 @@
# ChangeLog
## **next**
## **5.91.2** (2024-02-09)
<img id="latest" src="https://badgen.net/badge/channel/latest/yellow" alt="Channel: latest" />
### Enhancements
@@ -39,8 +41,6 @@
## **5.91.0** (2024-01-31)
<img id="latest" src="https://badgen.net/badge/channel/latest/yellow" alt="Channel: latest" />
### Highlights
- [Import/VMWare] Speed up import and make all imports thin [#7323](https://github.com/vatesfr/xen-orchestra/issues/7323)

View File

@@ -7,12 +7,16 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [Self service] From user POV, show used resources even when they are unlimited (PR [#7353](https://github.com/vatesfr/xen-orchestra/pull/7353))
- Disable search engine indexing via a `robots.txt`
### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed”
- [Settings/XO Config] Sort backups from newest to oldest
- [Plugins/audit] Don't log `tag.getAllConfigured` calls
- [Remotes] Correctly clear error when the remote is tested with success
### Packages to release
> When modifying a package, add it here with its release type.
@@ -29,7 +33,8 @@
<!--packages-start-->
- xo-server minor
- xo-web minor
- xo-server patch
- xo-server-audit patch
- xo-web patch
<!--packages-end-->

View File

@@ -1,4 +1,6 @@
{
"name": "xen-orchestra",
"version": "0.0.0",
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/eslint-parser": "^7.13.8",

View File

@@ -72,6 +72,7 @@ const DEFAULT_BLOCKED_LIST = {
'system.getServerTimezone': true,
'system.getServerVersion': true,
'system.getVersion': true,
'tag.getAllConfigured': true,
'test.getPermissionsForUser': true,
'user.getAll': true,
'user.getAuthenticationTokens': true,

View File

@@ -143,6 +143,7 @@ port = 80
requestTimeout = 0
[http.mounts]
'/robots.txt' = './robots.txt'
'/' = '../xo-web/dist/'
'/v6' = '../../@xen-orchestra/web/dist/'

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@@ -55,10 +55,10 @@ const normalize = set => ({
limits: set.limits
? map(set.limits, limit =>
isObject(limit)
? { ...limit, usage: limit.usage ?? 0 }
? limit
: {
available: limit,
total: limit,
usage: 0,
}
)
: {},
@@ -217,32 +217,25 @@ export default class {
if (objects) {
set.objects = objects
}
const previousLimits = set.limits
const newLimits = {}
forEach(limits, (quantity, id) => {
const previous = previousLimits[id]
if (previous !== undefined) {
newLimits[id] = {
total: quantity,
usage: previous.usage,
if (limits) {
const previousLimits = set.limits
set.limits = map(limits, (quantity, id) => {
const previous = previousLimits[id]
if (!previous) {
return {
available: quantity,
total: quantity,
}
}
} else {
newLimits[id] = {
const { available, total } = previous
return {
available: available - total + quantity,
total: quantity,
usage: 0,
}
}
})
const removedLimits = Object.keys(previousLimits).filter(key => !(key in newLimits))
removedLimits.forEach(id => {
newLimits[id] = {
usage: previousLimits[id].usage ?? 0,
}
})
set.limits = newLimits
})
}
if (ipPools) {
set.ipPools = ipPools
}
@@ -339,16 +332,15 @@ export default class {
forEach(limits, (quantity, id) => {
const limit = set.limits[id]
if (!limit) {
set.limits[id] = { usage: quantity }
return
}
if ((limit.usage += quantity) > limit.total && !force) {
if ((limit.available -= quantity) < 0 && !force) {
throw notEnoughResources([
{
resourceSet: setId,
resourceType: id,
available: limit.total - (limit.usage - quantity),
available: limit.available + quantity,
requested: quantity,
},
])
@@ -366,8 +358,8 @@ export default class {
return
}
if ((limit.usage -= quantity) < 0) {
limit.usage = 0
if ((limit.available += quantity) > limit.total) {
limit.available = limit.total
}
})
await this._save(set)
@@ -379,7 +371,7 @@ export default class {
forEach(limits, (limit, id) => {
if (VM_RESOURCES[id] || id.startsWith('ipPool:')) {
// only reset VMs related limits
limit.usage = 0
limit.available = limit.total
}
})
})
@@ -405,9 +397,7 @@ export default class {
forEach(await this.computeResourcesUsage(this._app.getObject(object.$id)), (usage, resource) => {
const limit = limits[resource]
if (limit) {
limit.usage += usage
} else {
limits[resource] = { usage }
limit.available -= usage
}
})
})

View File

@@ -31,10 +31,11 @@ export default class ResourceSetQuotas extends Component {
forEach(RESOURCES, resource => {
if (limits[resource] != null) {
const { total, usage } = limits[resource]
const { available, total } = limits[resource]
quotas[resource] = {
available,
total,
usage,
usage: total - available,
}
}
})
@@ -88,26 +89,22 @@ export default class ResourceSetQuotas extends Component {
<CardBlock className='text-center'>
{quota !== undefined ? (
<div>
{Number.isFinite(quota.total) ? (
<ChartistGraph
data={{
labels,
series: [quota.total - quota.usage, quota.usage],
}}
options={{
donut: true,
donutWidth: 40,
showLabel: false,
}}
type='Pie'
/>
) : (
<p className='text-xs-center display-1'>&infin;</p>
)}
<ChartistGraph
data={{
labels,
series: [quota.available, quota.usage],
}}
options={{
donut: true,
donutWidth: 40,
showLabel: false,
}}
type='Pie'
/>
<p className='text-xs-center'>
{_('resourceSetQuota', {
total: !Number.isFinite(quota.total) ? Infinity : formatSize(quota.total),
usage: validFormat ? quota.usage?.toString() : formatSize(quota.usage),
total: validFormat ? quota.total.toString() : formatSize(quota.total),
usage: validFormat ? quota.usage.toString() : formatSize(quota.usage),
})}
</p>
</div>

View File

@@ -1099,7 +1099,9 @@ export const SelectXoCloudConfig = makeSubscriptionSelect(
subscriber =>
subscribeCloudXoConfigBackups(configs => {
const xoObjects = groupBy(
map(configs, config => ({ ...config, type: 'xoConfig' })),
map(configs, config => ({ ...config, type: 'xoConfig' }))
// from newest to oldest
.sort((a, b) => b.createdAt - a.createdAt),
'xoaId'
)
subscriber({

View File

@@ -1870,21 +1870,29 @@ export default class NewVm extends BaseComponent {
{limits && (
<Row>
<Col size={3}>
{cpusLimits?.total !== undefined && (
<Limits limit={cpusLimits.total} toBeUsed={CPUs * factor} used={cpusLimits.usage} />
{cpusLimits && (
<Limits
limit={cpusLimits.total}
toBeUsed={CPUs * factor}
used={cpusLimits.total - cpusLimits.available}
/>
)}
</Col>
<Col size={3}>
{memoryLimits?.total !== undefined && (
<Limits limit={memoryLimits.total} toBeUsed={_memory * factor} used={memoryLimits.usage} />
{memoryLimits && (
<Limits
limit={memoryLimits.total}
toBeUsed={_memory * factor}
used={memoryLimits.total - memoryLimits.available}
/>
)}
</Col>
<Col size={3}>
{diskLimits?.total !== undefined && (
{diskLimits && (
<Limits
limit={diskLimits.total}
toBeUsed={(sumBy(VDIs, 'size') + sum(map(existingDisks, disk => disk.size))) * factor}
used={diskLimits.usage}
used={diskLimits.total - diskLimits.available}
/>
)}
</Col>
@@ -1915,10 +1923,10 @@ export default class NewVm extends BaseComponent {
const factor = multipleVms ? nameLabels.length : 1
return !(
CPUs * factor > get(() => resourceSet.limits.cpus.total - resourceSet.limits.cpus.usage) ||
_memory * factor > get(() => resourceSet.limits.memory.total - resourceSet.limits.memory.usage) ||
CPUs * factor > get(() => resourceSet.limits.cpus.available) ||
_memory * factor > get(() => resourceSet.limits.memory.available) ||
(sumBy(VDIs, 'size') + sum(map(existingDisks, disk => disk.size))) * factor >
get(() => resourceSet.limits.disk.total - resourceSet.limits.disk.usage)
get(() => resourceSet.limits.disk.available)
)
}
}

View File

@@ -5,10 +5,9 @@ import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import { confirm } from 'modal'
import { getApiApplianceInfo, subscribeCloudXoConfig, subscribeCloudXoConfigBackups } from 'xo'
import { groupBy, sortBy } from 'lodash'
import { injectState, provideState } from 'reaclette'
import { SelectXoCloudConfig } from 'select-objects'
import { subscribeCloudXoConfig, subscribeCloudXoConfigBackups } from 'xo'
import BackupXoConfigModal from './backup-xo-config-modal'
import RestoreXoConfigModal from './restore-xo-config-modal'
@@ -88,15 +87,7 @@ const CloudConfig = decorate([
},
},
computed: {
applianceId: async () => {
const { id } = await getApiApplianceInfo()
return id
},
groupedConfigs: ({ applianceId, sortedConfigs }) =>
sortBy(groupBy(sortedConfigs, 'xoaId'), config => (config[0].xoaId === applianceId ? -1 : 1)),
isConfigDefined: ({ config }) => config != null,
sortedConfigs: (_, { cloudXoConfigBackups }) =>
cloudXoConfigBackups?.sort((config, nextConfig) => config.createdAt - nextConfig.createdAt),
},
}),
injectState,

View File

@@ -33,7 +33,7 @@ const formatError = error => (typeof error === 'string' ? error : JSON.stringify
const _changeUrlElement = (value, { remote, element }) =>
editRemote(remote, {
url: format({ ...remote, [element]: value === null ? undefined : value }),
url: format({ ...parse(remote.url), [element]: value === null ? undefined : value }),
})
const _showError = remote => alert(_('remoteConnectionFailed'), <pre>{formatError(remote.error)}</pre>)
const _editRemoteName = (name, { remote }) => editRemote(remote, { name })