parent
ec4dde86f5
commit
6d39512576
@ -17,10 +17,10 @@ interface Record {
|
||||
}
|
||||
|
||||
export class AuditCore {
|
||||
constructor(storage: Storage) { }
|
||||
public add(subject: any, event: string, data: any): Promise<Record> { }
|
||||
public checkIntegrity(oldest: string, newest: string): Promise<number> { }
|
||||
public getFrom(newest?: string): AsyncIterator { }
|
||||
public deleteFrom(newest: string): Promise<void> { }
|
||||
public deleteRangeAndRewrite(newest: string, oldest: string): Promise<void> { }
|
||||
constructor(storage: Storage) {}
|
||||
public add(subject: any, event: string, data: any): Promise<Record> {}
|
||||
public checkIntegrity(oldest: string, newest: string): Promise<number> {}
|
||||
public getFrom(newest?: string): AsyncIterator {}
|
||||
public deleteFrom(newest: string): Promise<void> {}
|
||||
public deleteRangeAndRewrite(newest: string, oldest: string): Promise<void> {}
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ const { execFile } = require('child_process')
|
||||
const parse = createParser({
|
||||
keyTransform: key => key.slice(5).toLowerCase(),
|
||||
})
|
||||
const makeFunction = command => async (fields, ...args) => {
|
||||
const makeFunction =
|
||||
command =>
|
||||
async (fields, ...args) => {
|
||||
const info = await fromCallback(execFile, command, [
|
||||
'--noheading',
|
||||
'--nosuffix',
|
||||
@ -23,7 +25,7 @@ const makeFunction = command => async (fields, ...args) => {
|
||||
.trim()
|
||||
.split(/\r?\n/)
|
||||
.map(Array.isArray(fields) ? parse : line => parse(line)[fields])
|
||||
}
|
||||
}
|
||||
|
||||
exports.lvs = makeFunction('lvs')
|
||||
exports.pvs = makeFunction('pvs')
|
||||
|
@ -20,36 +20,8 @@ if (process.stdout !== undefined && process.stdout.isTTY && process.stderr !== u
|
||||
}
|
||||
|
||||
const NAMESPACE_COLORS = [
|
||||
196,
|
||||
202,
|
||||
208,
|
||||
214,
|
||||
220,
|
||||
226,
|
||||
190,
|
||||
154,
|
||||
118,
|
||||
82,
|
||||
46,
|
||||
47,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
45,
|
||||
39,
|
||||
33,
|
||||
27,
|
||||
21,
|
||||
57,
|
||||
93,
|
||||
129,
|
||||
165,
|
||||
201,
|
||||
200,
|
||||
199,
|
||||
198,
|
||||
197,
|
||||
196, 202, 208, 214, 220, 226, 190, 154, 118, 82, 46, 47, 48, 49, 50, 51, 45, 39, 33, 27, 21, 57, 93, 129, 165, 201,
|
||||
200, 199, 198, 197,
|
||||
]
|
||||
formatNamespace = namespace => {
|
||||
// https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
|
||||
|
@ -28,7 +28,8 @@ export default {
|
||||
buffer.toString('hex', offset + 5, offset + 6),
|
||||
|
||||
stringToEth: (string, buffer, offset) => {
|
||||
const eth = /^([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2})$/.exec(
|
||||
const eth =
|
||||
/^([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2})$/.exec(
|
||||
string
|
||||
)
|
||||
assert(eth !== null)
|
||||
@ -50,7 +51,8 @@ export default {
|
||||
),
|
||||
|
||||
stringToip4: (string, buffer, offset) => {
|
||||
const ip = /^([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])$/.exec(
|
||||
const ip =
|
||||
/^([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])$/.exec(
|
||||
string
|
||||
)
|
||||
assert(ip !== null)
|
||||
|
@ -36,7 +36,14 @@ async function main(argv) {
|
||||
|
||||
const { hostname = 'localhost', port } = config?.http?.listen?.https ?? {}
|
||||
|
||||
const { _: args, file, help, host, raw, token } = getopts(argv, {
|
||||
const {
|
||||
_: args,
|
||||
file,
|
||||
help,
|
||||
host,
|
||||
raw,
|
||||
token,
|
||||
} = getopts(argv, {
|
||||
alias: { file: 'f', help: 'h' },
|
||||
boolean: ['help', 'raw'],
|
||||
default: {
|
||||
|
@ -93,10 +93,7 @@ declare namespace event {
|
||||
snapshot: Task
|
||||
}
|
||||
|
||||
function from(_: {
|
||||
token: string = ''
|
||||
timeout?: number
|
||||
}): {
|
||||
function from(_: { token: string = ''; timeout?: number }): {
|
||||
events: Event[]
|
||||
token: string
|
||||
}
|
||||
@ -146,13 +143,13 @@ declare namespace backup {
|
||||
streamLogs: boolean = false
|
||||
}): string
|
||||
|
||||
function listPoolMetadataBackups(_: {
|
||||
remotes: { [id: string]: Remote }
|
||||
}): { [remoteId: string]: { [poolUuid: string]: object[] } }
|
||||
function listPoolMetadataBackups(_: { remotes: { [id: string]: Remote } }): {
|
||||
[remoteId: string]: { [poolUuid: string]: object[] }
|
||||
}
|
||||
|
||||
function listVmBackups(_: {
|
||||
remotes: { [remoteId: string]: Remote }
|
||||
}): { [remoteId: string]: { [vmUuid: string]: object[] } }
|
||||
function listVmBackups(_: { remotes: { [remoteId: string]: Remote } }): {
|
||||
[remoteId: string]: { [vmUuid: string]: object[] }
|
||||
}
|
||||
|
||||
function listXoMetadataBackups(_: { remotes: { [id: string]: Remote } }): { [remoteId: string]: object[] }
|
||||
|
||||
|
@ -320,6 +320,7 @@ You can learn more about XenServer [resource management on the Citrix Website](h
|
||||
:::tip
|
||||
XCP-ng doesn't limit VMs to 32 vCPU
|
||||
:::
|
||||
|
||||
### VDI live migration
|
||||
|
||||
Thanks to Xen Storage Motion, it's easy to move a VM disk from one storage location to another, while the VM is running! This feature can help you migrate from your local storage to a SAN, or just upgrade your SAN without any downtime.
|
||||
@ -491,10 +492,12 @@ If you are behind a proxy, please update your `xo-server` configuration to add a
|
||||
::: danger
|
||||
As specified in the [documentation](https://xcp-ng.org/docs/requirements.html#pool-requirements) your pool shouldn't consist of hosts from different CPU vendors.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
- Even with matching CPU vendors, in the case of different CPU models XCP-ng will scale the pool CPU ability to the CPU having the least instructions.
|
||||
- All the hosts in a pool must run the same XCP-ng version.
|
||||
:::
|
||||
|
||||
### Creating a pool
|
||||
|
||||
First you should add your new host to XOA by going to New > Server as described in [the relevant chapter](manage_infrastructure.md#add-a-host).
|
||||
|
@ -59,9 +59,11 @@ While creating a standard backup job from your main Xen Orchestra appliance, you
|
||||
|
||||
Login is disabled by default on proxy appliances.
|
||||
If you need to login for some reason, you need to set a password for the xoa user via the XenStore of the VM. The following is to be ran on your XCP-ng host:
|
||||
|
||||
```
|
||||
xe vm-param-set uuid=<UUID> xenstore-data:vm-data/system-account-xoa-password=<password>
|
||||
```
|
||||
|
||||
Where UUID is the uuid of your proxy VM.
|
||||
|
||||
Then you need to restart the proxy VM.
|
||||
@ -74,15 +76,19 @@ First you will need to add a second VIF to your Proxy VM. This can be done in th
|
||||
After adding the VIF you will need to set an IP for the new NIC, for that you will first need to SSH to the VM [as describe before](/proxy.md#enabling-login-to-proxy-appliance).
|
||||
|
||||
Then set the new IP:
|
||||
|
||||
```
|
||||
$ xoa network static eth1
|
||||
? Static IP for this machine 192.168.100.120
|
||||
? Network mask (eg 255.255.255.0) 255.255.255.0
|
||||
```
|
||||
|
||||
If you want to set a static address.
|
||||
|
||||
```
|
||||
$ xoa network dhcp eth1
|
||||
```
|
||||
|
||||
If you prefer using DHCP.
|
||||
:::tip
|
||||
As XOA uses the first IP address reported by XAPI to contact the proxy appliance, you may have to switch the network card order if you want your proxy to be connected through a specific IP address.
|
||||
|
@ -19,9 +19,11 @@ XOA uses HVM mode. If your physical host doesn't support virtualization extensio
|
||||
## Set or recover XOA VM password
|
||||
|
||||
As no password is set for the xoa system user by default, you will need to set your own. This can be done via the XenStore data of the VM. The following is to be ran on your XCP-ng host:
|
||||
|
||||
```
|
||||
xe vm-param-set uuid=<UUID> xenstore-data:vm-data/system-account-xoa-password=<password>
|
||||
```
|
||||
|
||||
Where UUID is the uuid of your XOA VM.
|
||||
|
||||
Then you need to restart the VM.
|
||||
|
@ -15,24 +15,28 @@ const authorized = () => true // eslint-disable-line no-unused-vars
|
||||
const forbiddden = () => false // eslint-disable-line no-unused-vars
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const and = (...checkers) => (object, permission) => {
|
||||
const and =
|
||||
(...checkers) =>
|
||||
(object, permission) => {
|
||||
for (const checker of checkers) {
|
||||
if (!checker(object, permission)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const or = (...checkers) => (object, permission) => {
|
||||
const or =
|
||||
(...checkers) =>
|
||||
(object, permission) => {
|
||||
for (const checker of checkers) {
|
||||
if (checker(object, permission)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
|
@ -7,10 +7,7 @@ import execPromise = require('exec-promise')
|
||||
import through2 = require('through2')
|
||||
import Xo from 'xo-lib'
|
||||
|
||||
const parseBoolean = (
|
||||
value: string,
|
||||
defaultValue?: boolean
|
||||
): boolean | undefined => {
|
||||
const parseBoolean = (value: string, defaultValue?: boolean): boolean | undefined => {
|
||||
if (value === undefined || value === '') {
|
||||
return defaultValue
|
||||
}
|
||||
@ -49,12 +46,7 @@ execPromise(
|
||||
const errors: any[] = []
|
||||
|
||||
const stream = process.stdin.pipe(csvParser()).pipe(
|
||||
through2.obj(
|
||||
(
|
||||
{ allowUnauthorized, autoConnect, host, label, password, username },
|
||||
_,
|
||||
next
|
||||
) => {
|
||||
through2.obj(({ allowUnauthorized, autoConnect, host, label, password, username }, _, next) => {
|
||||
console.log('server', host)
|
||||
|
||||
xo.call('server.add', {
|
||||
@ -71,8 +63,7 @@ execPromise(
|
||||
return next()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
|
@ -445,18 +445,14 @@ class Netbox {
|
||||
this.#makeRequest('/virtualization/interfaces/', 'DELETE', interfacesToDelete),
|
||||
isEmpty(interfacesToCreateByVif)
|
||||
? {}
|
||||
: this.#makeRequest(
|
||||
'/virtualization/interfaces/',
|
||||
'POST',
|
||||
Object.values(interfacesToCreateByVif)
|
||||
).then(interfaces => zipObject(Object.keys(interfacesToCreateByVif), interfaces)),
|
||||
: this.#makeRequest('/virtualization/interfaces/', 'POST', Object.values(interfacesToCreateByVif)).then(
|
||||
interfaces => zipObject(Object.keys(interfacesToCreateByVif), interfaces)
|
||||
),
|
||||
isEmpty(interfacesToUpdateByVif)
|
||||
? {}
|
||||
: this.#makeRequest(
|
||||
'/virtualization/interfaces/',
|
||||
'PATCH',
|
||||
Object.values(interfacesToUpdateByVif)
|
||||
).then(interfaces => zipObject(Object.keys(interfacesToUpdateByVif), interfaces)),
|
||||
: this.#makeRequest('/virtualization/interfaces/', 'PATCH', Object.values(interfacesToUpdateByVif)).then(
|
||||
interfaces => zipObject(Object.keys(interfacesToUpdateByVif), interfaces)
|
||||
),
|
||||
])
|
||||
)
|
||||
.slice(1)
|
||||
|
@ -24,7 +24,10 @@ getMethodsInfo.permission = null // user does not need to be authenticated
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const getServerTimezone = (tz => () => tz)(moment.tz.guess())
|
||||
export const getServerTimezone = (
|
||||
tz => () =>
|
||||
tz
|
||||
)(moment.tz.guess())
|
||||
getServerTimezone.description = 'return the timezone server'
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
@ -8,7 +8,7 @@ export default function globMatcher(patterns, opts) {
|
||||
if (!Array.isArray(patterns)) {
|
||||
if (patterns[0] === '!') {
|
||||
const m = matcher(patterns.slice(1), opts)
|
||||
return function(string) {
|
||||
return function (string) {
|
||||
return !m(string)
|
||||
}
|
||||
} else {
|
||||
@ -32,7 +32,7 @@ export default function globMatcher(patterns, opts) {
|
||||
const nNone = noneMustMatch.length
|
||||
const nAny = anyMustMatch.length
|
||||
|
||||
return function(string) {
|
||||
return function (string) {
|
||||
if (typeof string !== 'string') {
|
||||
return false
|
||||
}
|
||||
|
@ -40,7 +40,9 @@ export const mergeObjects = objects => Object.assign({}, ...objects)
|
||||
//
|
||||
// Ex: crossProduct([ [ { a: 2 }, { b: 3 } ], [ { c: 5 }, { d: 7 } ] ] )
|
||||
// => [ { a: 2, c: 5 }, { b: 3, c: 5 }, { a: 2, d: 7 }, { b: 3, d: 7 } ]
|
||||
export const crossProduct = (vectors, mergeFn = mergeObjects) => cb =>
|
||||
export const crossProduct =
|
||||
(vectors, mergeFn = mergeObjects) =>
|
||||
cb =>
|
||||
combine(vectors)(vector => {
|
||||
cb(mergeFn(vector))
|
||||
})
|
||||
|
@ -99,13 +99,16 @@ export default class Xapi extends XapiBase {
|
||||
this._snapshotVm = limitConcurrency(vmSnapshotConcurrency)(this._snapshotVm)
|
||||
|
||||
// Patch getObject to resolve _xapiId property.
|
||||
this.getObject = (getObject => (...args) => {
|
||||
this.getObject = (
|
||||
getObject =>
|
||||
(...args) => {
|
||||
let tmp
|
||||
if ((tmp = args[0]) != null && (tmp = tmp._xapiId) != null) {
|
||||
args[0] = tmp
|
||||
}
|
||||
return getObject.apply(this, args)
|
||||
})(this.getObject)
|
||||
}
|
||||
)(this.getObject)
|
||||
}
|
||||
|
||||
// Wait for an object to be in a given state.
|
||||
|
@ -68,7 +68,9 @@ export default class IpPools {
|
||||
|
||||
if (await store.has(id)) {
|
||||
await Promise.all(
|
||||
(await this._app.getAllResourceSets()).map(async set => {
|
||||
(
|
||||
await this._app.getAllResourceSets()
|
||||
).map(async set => {
|
||||
await this._app.removeLimitFromResourceSet(`ipPool:${id}`, set.id)
|
||||
return this._app.removeIpPoolFromResourceSet(id, set.id)
|
||||
})
|
||||
|
@ -177,7 +177,9 @@ export default class {
|
||||
await Promise.all(
|
||||
difference(set.subjects, subjects).map(async subjectId =>
|
||||
Promise.all(
|
||||
(await this._app.getAclsForSubject(subjectId)).map(async acl => {
|
||||
(
|
||||
await this._app.getAclsForSubject(subjectId)
|
||||
).map(async acl => {
|
||||
try {
|
||||
const object = this._app.getObject(acl.object)
|
||||
if ((object.type === 'VM' || object.type === 'VM-snapshot') && object.resourceSet === id) {
|
||||
|
@ -131,7 +131,9 @@ const CustomFields = decorate([
|
||||
}),
|
||||
provideState({
|
||||
effects: {
|
||||
addCustomField: () => (state, { object: { id } }) => {
|
||||
addCustomField:
|
||||
() =>
|
||||
(state, { object: { id } }) => {
|
||||
const dateTimeUtc = moment.utc()
|
||||
return form({
|
||||
component: CustomFieldModal,
|
||||
@ -149,9 +151,13 @@ const CustomFields = decorate([
|
||||
),
|
||||
}).then(params => checkParamsAndCallMethod(addCustomField, id, params))
|
||||
},
|
||||
removeCustomField: (_, { currentTarget: { dataset } }) => (_, { object: { id } }) =>
|
||||
removeCustomField:
|
||||
(_, { currentTarget: { dataset } }) =>
|
||||
(_, { object: { id } }) =>
|
||||
removeCustomField(id, dataset.name),
|
||||
setCustomField: (effects, { name, value }) => (state, { object: { id } }) => {
|
||||
setCustomField:
|
||||
(effects, { name, value }) =>
|
||||
(state, { object: { id } }) => {
|
||||
const isDate = PATTERN_DATE_TIME_UTC.test(value)
|
||||
const dateTimeUtc = isDate ? moment(value).utc() : undefined
|
||||
return form({
|
||||
|
@ -4,13 +4,17 @@ export const generateId = () => `i${Math.random().toString(36).slice(2)}`
|
||||
|
||||
// TODO: remove these functions once the PR: https://github.com/JsCommunity/reaclette/pull/5 has been merged
|
||||
// It only supports native inputs
|
||||
export const linkState = (_, { target }) => () => ({
|
||||
export const linkState =
|
||||
(_, { target }) =>
|
||||
() => ({
|
||||
[target.name]:
|
||||
target.nodeName.toLowerCase() === 'input' && target.type.toLowerCase() === 'checkbox'
|
||||
? target.checked
|
||||
: target.value,
|
||||
})
|
||||
})
|
||||
|
||||
export const toggleState = (_, { currentTarget: { name } }) => state => ({
|
||||
export const toggleState =
|
||||
(_, { currentTarget: { name } }) =>
|
||||
state => ({
|
||||
[name]: !state[name],
|
||||
})
|
||||
})
|
||||
|
@ -635,7 +635,10 @@ const renderXoItem = (item, { className, type: xoType, ...props } = {}) => {
|
||||
|
||||
export { renderXoItem as default }
|
||||
|
||||
export const getRenderXoItemOfType = type => (item, options = {}) => renderXoItem(item, { ...options, type })
|
||||
export const getRenderXoItemOfType =
|
||||
type =>
|
||||
(item, options = {}) =>
|
||||
renderXoItem(item, { ...options, type })
|
||||
|
||||
const GenericXoItem = connectStore(() => {
|
||||
const getObject = createGetObject()
|
||||
|
@ -193,7 +193,9 @@ class ToggleTd extends Component {
|
||||
const TableSelect = decorate([
|
||||
provideState({
|
||||
effects: {
|
||||
onChange: (_, tdId, add) => (_, { value, onChange, options }) => {
|
||||
onChange:
|
||||
(_, tdId, add) =>
|
||||
(_, { value, onChange, options }) => {
|
||||
let newValue = [...value]
|
||||
const index = sortedIndex(newValue, tdId)
|
||||
if (add) {
|
||||
@ -207,7 +209,9 @@ const TableSelect = decorate([
|
||||
}
|
||||
onChange(newValue)
|
||||
},
|
||||
selectAll: () => ({ optionsValues }, { onChange }) => {
|
||||
selectAll:
|
||||
() =>
|
||||
({ optionsValues }, { onChange }) => {
|
||||
onChange(optionsValues)
|
||||
},
|
||||
},
|
||||
@ -258,7 +262,9 @@ TableSelect.propTypes = {
|
||||
const TimePicker = decorate([
|
||||
provideState({
|
||||
effects: {
|
||||
onChange: (_, value) => ({ optionsValues }, { onChange }) => {
|
||||
onChange:
|
||||
(_, value) =>
|
||||
({ optionsValues }, { onChange }) => {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.length === optionsValues.length ? '*' : value.join(',')
|
||||
} else {
|
||||
|
@ -524,7 +524,9 @@ export const SelectTag = decorate([
|
||||
editing: false,
|
||||
}),
|
||||
effects: {
|
||||
addTag: (effects, newTag) => ({ value }, { multi, onChange }) => {
|
||||
addTag:
|
||||
(effects, newTag) =>
|
||||
({ value }, { multi, onChange }) => {
|
||||
if (newTag === value || (multi && includes(value, newTag))) {
|
||||
return
|
||||
}
|
||||
|
@ -287,7 +287,9 @@ export const isAdmin = (...args) => {
|
||||
// Common selector creators.
|
||||
|
||||
// Creates an object selector from an id selector.
|
||||
export const createGetObject = (idSelector = _getId) => (state, props, useResourceSet) => {
|
||||
export const createGetObject =
|
||||
(idSelector = _getId) =>
|
||||
(state, props, useResourceSet) => {
|
||||
const object = state.objects.all[idSelector(state, props)]
|
||||
if (!object) {
|
||||
return
|
||||
@ -311,7 +313,7 @@ export const createGetObject = (idSelector = _getId) => (state, props, useResour
|
||||
if (predicate(object)) {
|
||||
return object
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Specialized createSort() configured for a given type.
|
||||
export const createSortForType = invoke(() => {
|
||||
@ -462,7 +464,10 @@ export const createDoesHostNeedRestart = hostSelector => {
|
||||
const patchRequiresReboot = createGetObjectsOfType('patch')
|
||||
.pick(create(hostSelector, host => host.patches))
|
||||
.find(
|
||||
create(hostSelector, host => ({ guidance, time, upgrade }) =>
|
||||
create(
|
||||
hostSelector,
|
||||
host =>
|
||||
({ guidance, time, upgrade }) =>
|
||||
time > host.startTime &&
|
||||
(upgrade || some(guidance, action => action === 'restartHost' || action === 'restartXapi'))
|
||||
)
|
||||
|
@ -140,7 +140,10 @@ const Action = decorate([
|
||||
computed: {
|
||||
disabled: ({ items }, { disabled, userData }) =>
|
||||
typeof disabled === 'function' ? disabled(items, userData) : disabled,
|
||||
handler: ({ items }, { handler, userData }) => () => handler(items, userData),
|
||||
handler:
|
||||
({ items }, { handler, userData }) =>
|
||||
() =>
|
||||
handler(items, userData),
|
||||
icon: ({ items }, { icon, userData }) => (typeof icon === 'function' ? icon(items, userData) : icon),
|
||||
items: (_, { items, grouped }) => (Array.isArray(items) || !grouped ? items : [items]),
|
||||
label: ({ items }, { label, userData }) => (typeof label === 'function' ? label(items, userData) : label),
|
||||
|
@ -7,7 +7,9 @@ import * as actions from './actions'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const createAsyncHandler = ({ error, next }) => (state, payload, action) => {
|
||||
const createAsyncHandler =
|
||||
({ error, next }) =>
|
||||
(state, payload, action) => {
|
||||
if (action.error) {
|
||||
if (error) {
|
||||
return error(state, payload, action)
|
||||
@ -19,7 +21,7 @@ const createAsyncHandler = ({ error, next }) => (state, payload, action) => {
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
// Action handlers are reducers but bound to a specific action.
|
||||
const combineActionHandlers = invoke(
|
||||
|
@ -576,7 +576,9 @@ export const downloadLog = ({ log, date, type }) => {
|
||||
// ])
|
||||
// )
|
||||
// ```
|
||||
export const createCompare = criterias => (...items) => {
|
||||
export const createCompare =
|
||||
criterias =>
|
||||
(...items) => {
|
||||
let res = 0
|
||||
// Array.find to stop when the result is != 0
|
||||
criterias.find(fn => {
|
||||
@ -587,7 +589,7 @@ export const createCompare = criterias => (...items) => {
|
||||
return (res = v1 < v2 ? -1 : v1 > v2 ? 1 : 0)
|
||||
})
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
|
@ -1368,7 +1368,9 @@ export const createVms = (args, nameLabels, cloudConfigs) =>
|
||||
body: _('newVmCreateVmsConfirm', { nbVms: nameLabels.length }),
|
||||
}).then(() =>
|
||||
Promise.all(
|
||||
map(nameLabels, (
|
||||
map(
|
||||
nameLabels,
|
||||
(
|
||||
name_label, // eslint-disable-line camelcase
|
||||
i
|
||||
) =>
|
||||
|
@ -25,7 +25,14 @@ import { ejectCd, isSrWritable, setDefaultSr } from 'xo'
|
||||
return {
|
||||
pool: getPool,
|
||||
poolMaster: createGetObject(createSelector(getPool, ({ master }) => master)),
|
||||
vbds: createGetObjectsOfType('VBD').filter(createSelector(getPool, ({ id }) => vbd => vbd.$pool === id)),
|
||||
vbds: createGetObjectsOfType('VBD').filter(
|
||||
createSelector(
|
||||
getPool,
|
||||
({ id }) =>
|
||||
vbd =>
|
||||
vbd.$pool === id
|
||||
)
|
||||
),
|
||||
}
|
||||
},
|
||||
{ withRef: true }
|
||||
|
@ -70,7 +70,9 @@ export default class MigrateVmModalBody extends BaseComponent {
|
||||
|
||||
this._getHostPredicate = createSelector(
|
||||
() => this.props.vm,
|
||||
({ $container }) => host => host.id !== $container
|
||||
({ $container }) =>
|
||||
host =>
|
||||
host.id !== $container
|
||||
)
|
||||
|
||||
this._getSrPredicate = createSelector(
|
||||
|
@ -11,7 +11,11 @@ import { Sr } from '../../render-xo-item'
|
||||
@connectStore(
|
||||
{
|
||||
srIds: createSelector(
|
||||
createGetObjectsOfType('PBD').filter((_, { hostIds }) => pbd => hostIds.includes(pbd.host)),
|
||||
createGetObjectsOfType('PBD').filter(
|
||||
(_, { hostIds }) =>
|
||||
pbd =>
|
||||
hostIds.includes(pbd.host)
|
||||
),
|
||||
pbds => {
|
||||
const srIds = new Set([])
|
||||
for (const id in pbds) {
|
||||
|
@ -8,7 +8,11 @@ import { isLatestXosanPackInstalled, connectStore, findLatestPack } from 'utils'
|
||||
|
||||
@connectStore(
|
||||
{
|
||||
hosts: createGetObjectsOfType('host').filter((_, { pool }) => host => host.$pool === pool.id),
|
||||
hosts: createGetObjectsOfType('host').filter(
|
||||
(_, { pool }) =>
|
||||
host =>
|
||||
host.$pool === pool.id
|
||||
),
|
||||
},
|
||||
{ withRef: true }
|
||||
)
|
||||
|
@ -264,17 +264,8 @@ export default class RestoreFileModalBody extends Component {
|
||||
|
||||
render() {
|
||||
const { backups } = this.props
|
||||
const {
|
||||
backup,
|
||||
disk,
|
||||
partition,
|
||||
partitions,
|
||||
path,
|
||||
scanDiskError,
|
||||
listFilesError,
|
||||
scanningFiles,
|
||||
selectedFiles,
|
||||
} = this.state
|
||||
const { backup, disk, partition, partitions, path, scanDiskError, listFilesError, scanningFiles, selectedFiles } =
|
||||
this.state
|
||||
const noPartitions = isEmpty(partitions)
|
||||
const redundantFiles = this._getRedundantFiles()
|
||||
|
||||
|
@ -93,14 +93,18 @@ const Schedules = decorate([
|
||||
delete settings[id]
|
||||
props.handlerSettings(settings)
|
||||
},
|
||||
showModal: (effects, { id = generateRandomId(), name, cron, timezone } = DEFAULT_SCHEDULE) => async (
|
||||
state,
|
||||
props
|
||||
) => {
|
||||
showModal:
|
||||
(effects, { id = generateRandomId(), name, cron, timezone } = DEFAULT_SCHEDULE) =>
|
||||
async (state, props) => {
|
||||
const schedule = get(() => props.schedules[id])
|
||||
const setting = get(() => props.settings[id])
|
||||
|
||||
const { cron: newCron, name: newName, timezone: newTimezone, ...newSetting } = await form({
|
||||
const {
|
||||
cron: newCron,
|
||||
name: newName,
|
||||
timezone: newTimezone,
|
||||
...newSetting
|
||||
} = await form({
|
||||
defaultValue: setDefaultRetentions({ cron, name, timezone, ...setting }, state.retentions),
|
||||
render: props => <NewSchedule retentions={state.retentions} {...props} />,
|
||||
header: (
|
||||
@ -135,7 +139,9 @@ const Schedules = decorate([
|
||||
},
|
||||
})
|
||||
},
|
||||
toggleScheduleState: (_, id) => (state, { handlerSchedules, schedules }) => {
|
||||
toggleScheduleState:
|
||||
(_, id) =>
|
||||
(state, { handlerSchedules, schedules }) => {
|
||||
const schedule = schedules[id]
|
||||
handlerSchedules({
|
||||
...schedules,
|
||||
@ -148,7 +154,9 @@ const Schedules = decorate([
|
||||
},
|
||||
computed: {
|
||||
columns: (_, { retentions }) => [...COLUMNS, ...retentions.map(({ defaultValue, ...props }) => props)],
|
||||
rowTransform: (_, { settings = {}, retentions }) => schedule => {
|
||||
rowTransform:
|
||||
(_, { settings = {}, retentions }) =>
|
||||
schedule => {
|
||||
schedule = { ...schedule, ...settings[schedule.id] }
|
||||
return setDefaultRetentions(schedule, retentions)
|
||||
},
|
||||
|
@ -14,19 +14,25 @@ import { areRetentionsMissing } from '.'
|
||||
export default decorate([
|
||||
provideState({
|
||||
effects: {
|
||||
setSchedule: (_, params) => (_, { value, onChange }) => {
|
||||
setSchedule:
|
||||
(_, params) =>
|
||||
(_, { value, onChange }) => {
|
||||
onChange({
|
||||
...value,
|
||||
...params,
|
||||
})
|
||||
},
|
||||
setCronTimezone: ({ setSchedule }, { cronPattern: cron, timezone }) => () => {
|
||||
setCronTimezone:
|
||||
({ setSchedule }, { cronPattern: cron, timezone }) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
cron,
|
||||
timezone,
|
||||
})
|
||||
},
|
||||
setName: ({ setSchedule }, { target: { value } }) => () => {
|
||||
setName:
|
||||
({ setSchedule }, { target: { value } }) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
name: value.trim() === '' ? null : value,
|
||||
})
|
||||
|
@ -212,7 +212,8 @@ const DeleteOldBackupsFirst = ({ handler, handlerParam, value }) => (
|
||||
)
|
||||
|
||||
const New = decorate([
|
||||
New => props => (
|
||||
New => props =>
|
||||
(
|
||||
<Upgrade place='newBackup' required={2}>
|
||||
<New {...props} />
|
||||
</Upgrade>
|
||||
@ -349,11 +350,15 @@ const New = decorate([
|
||||
vms: state.smartMode ? state.vmsSmartPattern : constructPattern(state.vms),
|
||||
})
|
||||
},
|
||||
toggleMode: (_, { mode }) => state => ({
|
||||
toggleMode:
|
||||
(_, { mode }) =>
|
||||
state => ({
|
||||
...state,
|
||||
[mode]: !state[mode],
|
||||
}),
|
||||
setCheckboxValue: (_, { target: { checked, name } }) => state => ({
|
||||
setCheckboxValue:
|
||||
(_, { target: { checked, name } }) =>
|
||||
state => ({
|
||||
...state,
|
||||
[name]: checked,
|
||||
}),
|
||||
@ -367,11 +372,15 @@ const New = decorate([
|
||||
},
|
||||
},
|
||||
}),
|
||||
setName: (_, { target: { value } }) => state => ({
|
||||
setName:
|
||||
(_, { target: { value } }) =>
|
||||
state => ({
|
||||
...state,
|
||||
name: value,
|
||||
}),
|
||||
setTargetDeleteFirst: (_, id) => ({ propSettings, settings = propSettings }) => ({
|
||||
setTargetDeleteFirst:
|
||||
(_, id) =>
|
||||
({ propSettings, settings = propSettings }) => ({
|
||||
settings: settings.set(id, {
|
||||
deleteFirst: !settings.getIn([id, 'deleteFirst']),
|
||||
}),
|
||||
@ -403,7 +412,9 @@ const New = decorate([
|
||||
}
|
||||
},
|
||||
setVms: (_, vms) => state => ({ ...state, vms }),
|
||||
updateParams: () => (_, { job, schedules }) => {
|
||||
updateParams:
|
||||
() =>
|
||||
(_, { job, schedules }) => {
|
||||
const remotes = job.remotes !== undefined ? destructPattern(job.remotes) : []
|
||||
const srs = job.srs !== undefined ? destructPattern(job.srs) : []
|
||||
|
||||
@ -421,7 +432,9 @@ const New = decorate([
|
||||
...destructVmsPattern(job.vms),
|
||||
}
|
||||
},
|
||||
showScheduleModal: ({ saveSchedule }, storedSchedule = DEFAULT_SCHEDULE) => async (
|
||||
showScheduleModal:
|
||||
({ saveSchedule }, storedSchedule = DEFAULT_SCHEDULE) =>
|
||||
async (
|
||||
{ copyMode, exportMode, deltaMode, isDelta, propSettings, settings = propSettings, snapshotMode },
|
||||
{ intl: { formatMessage } }
|
||||
) => {
|
||||
@ -461,7 +474,9 @@ const New = decorate([
|
||||
id: storedSchedule.id || generateRandomId(),
|
||||
})
|
||||
},
|
||||
deleteSchedule: (_, schedule) => ({ schedules: oldSchedules, propSettings, settings = propSettings }) => {
|
||||
deleteSchedule:
|
||||
(_, schedule) =>
|
||||
({ schedules: oldSchedules, propSettings, settings = propSettings }) => {
|
||||
const id = resolveId(schedule)
|
||||
const schedules = { ...oldSchedules }
|
||||
delete schedules[id]
|
||||
@ -470,10 +485,12 @@ const New = decorate([
|
||||
settings: settings.delete(id),
|
||||
}
|
||||
},
|
||||
saveSchedule: (
|
||||
saveSchedule:
|
||||
(
|
||||
_,
|
||||
{ copyRetention, cron, enabled = true, exportRetention, fullInterval, id, name, snapshotRetention, timezone }
|
||||
) => ({ propSettings, schedules, settings = propSettings }) => ({
|
||||
) =>
|
||||
({ propSettings, schedules, settings = propSettings }) => ({
|
||||
schedules: {
|
||||
...schedules,
|
||||
[id]: {
|
||||
@ -509,7 +526,9 @@ const New = decorate([
|
||||
notValues,
|
||||
},
|
||||
}),
|
||||
resetJob: ({ updateParams }) => (state, { job }) => {
|
||||
resetJob:
|
||||
({ updateParams }) =>
|
||||
(state, { job }) => {
|
||||
if (job !== undefined) {
|
||||
updateParams()
|
||||
}
|
||||
@ -520,10 +539,14 @@ const New = decorate([
|
||||
setProxy(_, id) {
|
||||
this.state._proxyId = id
|
||||
},
|
||||
toggleDisplayAdvancedSettings: () => ({ displayAdvancedSettings }) => ({
|
||||
toggleDisplayAdvancedSettings:
|
||||
() =>
|
||||
({ displayAdvancedSettings }) => ({
|
||||
_displayAdvancedSettings: !displayAdvancedSettings,
|
||||
}),
|
||||
setGlobalSettings: (_, globalSettings) => ({ propSettings, settings = propSettings }) => ({
|
||||
setGlobalSettings:
|
||||
(_, globalSettings) =>
|
||||
({ propSettings, settings = propSettings }) => ({
|
||||
settings: settings.update('', setting => ({
|
||||
...setting,
|
||||
...globalSettings,
|
||||
@ -545,17 +568,23 @@ const New = decorate([
|
||||
reportRecipients: (reportRecipients.splice(key, 1), reportRecipients),
|
||||
})
|
||||
},
|
||||
setReportWhen: ({ setGlobalSettings }, { value }) => () => {
|
||||
setReportWhen:
|
||||
({ setGlobalSettings }, { value }) =>
|
||||
() => {
|
||||
setGlobalSettings({
|
||||
reportWhen: value,
|
||||
})
|
||||
},
|
||||
setConcurrency: ({ setGlobalSettings }, concurrency) => () => {
|
||||
setConcurrency:
|
||||
({ setGlobalSettings }, concurrency) =>
|
||||
() => {
|
||||
setGlobalSettings({
|
||||
concurrency,
|
||||
})
|
||||
},
|
||||
setTimeout: ({ setGlobalSettings }, value) => () => {
|
||||
setTimeout:
|
||||
({ setGlobalSettings }, value) =>
|
||||
() => {
|
||||
setGlobalSettings({
|
||||
timeout: value && value * 3600e3,
|
||||
})
|
||||
@ -565,7 +594,9 @@ const New = decorate([
|
||||
fullInterval,
|
||||
})
|
||||
},
|
||||
setOfflineBackup: ({ setGlobalSettings }, { target: { checked: offlineBackup } }) => () => {
|
||||
setOfflineBackup:
|
||||
({ setGlobalSettings }, { target: { checked: offlineBackup } }) =>
|
||||
() => {
|
||||
setGlobalSettings({
|
||||
offlineBackup,
|
||||
})
|
||||
@ -618,7 +649,9 @@ const New = decorate([
|
||||
...vmsPattern,
|
||||
tags: constructSmartPattern(tags, normalizeTagValues),
|
||||
}),
|
||||
vmPredicate: ({ isDelta }, { hostsById, poolsById }) => ({ $container }) =>
|
||||
vmPredicate:
|
||||
({ isDelta }, { hostsById, poolsById }) =>
|
||||
({ $container }) =>
|
||||
!isDelta ||
|
||||
canDeltaBackup(
|
||||
get(() => hostsById[$container].version) || get(() => hostsById[poolsById[$container].master].version)
|
||||
@ -639,8 +672,13 @@ const New = decorate([
|
||||
)
|
||||
)
|
||||
},
|
||||
srPredicate: ({ srs }) => sr => isSrWritable(sr) && !includes(srs, sr.id),
|
||||
remotePredicate: ({ proxyId, remotes }) => remote => {
|
||||
srPredicate:
|
||||
({ srs }) =>
|
||||
sr =>
|
||||
isSrWritable(sr) && !includes(srs, sr.id),
|
||||
remotePredicate:
|
||||
({ proxyId, remotes }) =>
|
||||
remote => {
|
||||
if (proxyId === null) {
|
||||
proxyId = undefined
|
||||
}
|
||||
|
@ -72,7 +72,8 @@ const getInitialState = () => ({
|
||||
})
|
||||
|
||||
export default decorate([
|
||||
New => props => (
|
||||
New => props =>
|
||||
(
|
||||
<Upgrade place='newMetadataBackup' required={3}>
|
||||
<New {...props} />
|
||||
</Upgrade>
|
||||
@ -166,7 +167,9 @@ export default decorate([
|
||||
setSettings: (_, _settings) => () => ({
|
||||
_settings,
|
||||
}),
|
||||
setGlobalSettings: ({ setSettings }, name, value) => ({ settings = {} }) => {
|
||||
setGlobalSettings:
|
||||
({ setSettings }, name, value) =>
|
||||
({ settings = {} }) => {
|
||||
setSettings({
|
||||
...settings,
|
||||
[GLOBAL_SETTING_KEY]: {
|
||||
@ -178,10 +181,14 @@ export default decorate([
|
||||
setReportWhen({ setGlobalSettings }, { value }) {
|
||||
setGlobalSettings('reportWhen', value)
|
||||
},
|
||||
toggleMode: (_, { mode }) => state => ({
|
||||
toggleMode:
|
||||
(_, { mode }) =>
|
||||
state => ({
|
||||
[`_${mode}`]: !state[mode],
|
||||
}),
|
||||
addRemote: (_, { id }) => state => ({
|
||||
addRemote:
|
||||
(_, { id }) =>
|
||||
state => ({
|
||||
_remotes: [...state.remotes, id],
|
||||
}),
|
||||
deleteRemote: (_, key) => state => {
|
||||
@ -227,7 +234,9 @@ export default decorate([
|
||||
})
|
||||
),
|
||||
remotes: ({ _remotes }, { job }) => defined(_remotes, () => destructPattern(job.remotes), []),
|
||||
remotesPredicate: ({ proxyId, remotes }) => remote => {
|
||||
remotesPredicate:
|
||||
({ proxyId, remotes }) =>
|
||||
remote => {
|
||||
if (proxyId === null) {
|
||||
proxyId = undefined
|
||||
}
|
||||
|
@ -20,34 +20,46 @@ const New = decorate([
|
||||
idInputName: generateId,
|
||||
},
|
||||
effects: {
|
||||
setSchedule: (_, params) => (_, { value, onChange }) => {
|
||||
setSchedule:
|
||||
(_, params) =>
|
||||
(_, { value, onChange }) => {
|
||||
onChange({
|
||||
...value,
|
||||
...params,
|
||||
})
|
||||
},
|
||||
setExportRetention: ({ setSchedule }, exportRetention) => () => {
|
||||
setExportRetention:
|
||||
({ setSchedule }, exportRetention) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
exportRetention,
|
||||
})
|
||||
},
|
||||
setCopyRetention: ({ setSchedule }, copyRetention) => () => {
|
||||
setCopyRetention:
|
||||
({ setSchedule }, copyRetention) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
copyRetention,
|
||||
})
|
||||
},
|
||||
setSnapshotRetention: ({ setSchedule }, snapshotRetention) => () => {
|
||||
setSnapshotRetention:
|
||||
({ setSchedule }, snapshotRetention) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
snapshotRetention,
|
||||
})
|
||||
},
|
||||
setCronTimezone: ({ setSchedule }, { cronPattern: cron, timezone }) => () => {
|
||||
setCronTimezone:
|
||||
({ setSchedule }, { cronPattern: cron, timezone }) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
cron,
|
||||
timezone,
|
||||
})
|
||||
},
|
||||
setName: ({ setSchedule }, { target: { value } }) => () => {
|
||||
setName:
|
||||
({ setSchedule }, { target: { value } }) =>
|
||||
() => {
|
||||
setSchedule({
|
||||
name: value.trim() === '' ? null : value,
|
||||
})
|
||||
|
@ -39,7 +39,9 @@ export default decorate([
|
||||
level: 'danger',
|
||||
},
|
||||
],
|
||||
rowTransform: ({ propSettings, settings = propSettings }) => schedule => ({
|
||||
rowTransform:
|
||||
({ propSettings, settings = propSettings }) =>
|
||||
schedule => ({
|
||||
...schedule,
|
||||
...settings.get(schedule.id),
|
||||
}),
|
||||
|
@ -27,7 +27,9 @@ const SmartBackup = decorate([
|
||||
}),
|
||||
provideState({
|
||||
effects: {
|
||||
setPattern: (_, value) => (_, { pattern, onChange }) => {
|
||||
setPattern:
|
||||
(_, value) =>
|
||||
(_, { pattern, onChange }) => {
|
||||
onChange({
|
||||
...pattern,
|
||||
...value,
|
||||
@ -38,7 +40,9 @@ const SmartBackup = decorate([
|
||||
power_state: powerState === 'All' ? undefined : powerState,
|
||||
})
|
||||
},
|
||||
setPoolPattern: ({ setPattern }, { values, notValues }) => ({ pools }) => {
|
||||
setPoolPattern:
|
||||
({ setPattern }, { values, notValues }) =>
|
||||
({ pools }) => {
|
||||
setPattern({
|
||||
$pool: constructSmartPattern(
|
||||
{
|
||||
@ -57,7 +61,9 @@ const SmartBackup = decorate([
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
poolPredicate: (_, { deltaMode, hosts }) => pool =>
|
||||
poolPredicate:
|
||||
(_, { deltaMode, hosts }) =>
|
||||
pool =>
|
||||
!deltaMode || canDeltaBackup(get(() => hosts[pool.master].version)),
|
||||
pools: (_, { pattern }) => (pattern.$pool !== undefined ? destructSmartPattern(pattern.$pool) : {}),
|
||||
},
|
||||
|
@ -238,7 +238,9 @@ const AttachedVdisTable = decorate([
|
||||
vdis: createGetObjectsOfType('VDI'),
|
||||
vdiSnapshots: createGetObjectsOfType('VDI-snapshot'),
|
||||
}),
|
||||
({ columns, rowTransform }) => ({ pools, srs, vbds, vdis, vdiSnapshots }) => (
|
||||
({ columns, rowTransform }) =>
|
||||
({ pools, srs, vbds, vdis, vdiSnapshots }) =>
|
||||
(
|
||||
<NoObjects
|
||||
actions={CONTROL_DOMAIN_VDIS_ACTIONS}
|
||||
collection={vbds}
|
||||
|
@ -71,7 +71,9 @@ const DiskImport = decorate([
|
||||
)
|
||||
return { disks: disks.filter(disk => disk !== undefined), loadingDisks: false }
|
||||
},
|
||||
import: () => async ({ disks, mapDescriptions, mapNames, sr }) => {
|
||||
import:
|
||||
() =>
|
||||
async ({ disks, mapDescriptions, mapNames, sr }) => {
|
||||
await importDisks(
|
||||
disks.map(({ id, name, ...disk }) => ({
|
||||
...disk,
|
||||
@ -82,11 +84,15 @@ const DiskImport = decorate([
|
||||
)
|
||||
},
|
||||
linkState,
|
||||
onChangeDescription: (_, { target: { name, value } }) => ({ mapDescriptions }) => {
|
||||
onChangeDescription:
|
||||
(_, { target: { name, value } }) =>
|
||||
({ mapDescriptions }) => {
|
||||
mapDescriptions[name] = value
|
||||
return { mapDescriptions }
|
||||
},
|
||||
onChangeName: (_, { target: { name, value } }) => ({ mapNames }) => {
|
||||
onChangeName:
|
||||
(_, { target: { name, value } }) =>
|
||||
({ mapNames }) => {
|
||||
mapNames[name] = value
|
||||
return { mapNames }
|
||||
},
|
||||
|
@ -928,14 +928,8 @@ export default class Home extends Component {
|
||||
} = this.state
|
||||
|
||||
const options = OPTIONS[type]
|
||||
const {
|
||||
filters,
|
||||
mainActions,
|
||||
otherActions,
|
||||
showHostsSelector,
|
||||
showPoolsSelector,
|
||||
showResourceSetsSelector,
|
||||
} = options
|
||||
const { filters, mainActions, otherActions, showHostsSelector, showPoolsSelector, showResourceSetsSelector } =
|
||||
options
|
||||
|
||||
return (
|
||||
<Container>
|
||||
|
@ -53,18 +53,32 @@ const isRunning = host => host && host.power_state === 'Running'
|
||||
const getPool = createGetObject((state, props) => getHost(state, props).$pool)
|
||||
|
||||
const getVmController = createGetObjectsOfType('VM-controller').find(
|
||||
createSelector(getHost, ({ id }) => obj => obj.$container === id)
|
||||
createSelector(
|
||||
getHost,
|
||||
({ id }) =>
|
||||
obj =>
|
||||
obj.$container === id
|
||||
)
|
||||
)
|
||||
|
||||
const getHostVms = createGetObjectsOfType('VM').filter(
|
||||
createSelector(getHost, ({ id }) => obj => obj.$container === id)
|
||||
createSelector(
|
||||
getHost,
|
||||
({ id }) =>
|
||||
obj =>
|
||||
obj.$container === id
|
||||
)
|
||||
)
|
||||
|
||||
const getNumberOfVms = getHostVms.count()
|
||||
|
||||
const getLogs = createGetObjectsOfType('message')
|
||||
.filter(
|
||||
createSelector(getHost, getVmController, (host, controller) => ({ $object }) =>
|
||||
createSelector(
|
||||
getHost,
|
||||
getVmController,
|
||||
(host, controller) =>
|
||||
({ $object }) =>
|
||||
$object === host.id || $object === (controller !== undefined && controller.id)
|
||||
)
|
||||
)
|
||||
|
@ -88,7 +88,9 @@ const SetControlDomainMemory = ({ value, onChange }) => (
|
||||
|
||||
const MultipathableSrs = decorate([
|
||||
connectStore({
|
||||
pbds: createGetObjectsOfType('PBD').filter((_, { hostId }) => pbd =>
|
||||
pbds: createGetObjectsOfType('PBD').filter(
|
||||
(_, { hostId }) =>
|
||||
pbd =>
|
||||
pbd.host === hostId && Boolean(pbd.otherConfig.multipathed)
|
||||
),
|
||||
}),
|
||||
|
@ -44,8 +44,14 @@ export default decorate([
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
networkPredicate: (_, { value: { pool } }) => network => pool.id === network.$pool,
|
||||
srPredicate: (_, { value }) => sr => sr.$pool === get(() => value.pool.id) && isSrWritable(sr),
|
||||
networkPredicate:
|
||||
(_, { value: { pool } }) =>
|
||||
network =>
|
||||
pool.id === network.$pool,
|
||||
srPredicate:
|
||||
(_, { value }) =>
|
||||
sr =>
|
||||
sr.$pool === get(() => value.pool.id) && isSrWritable(sr),
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
|
@ -47,15 +47,8 @@ export default decorate([
|
||||
}),
|
||||
effects: {
|
||||
async install() {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
namespace,
|
||||
markHubResourceAsInstalled,
|
||||
markHubResourceAsInstalling,
|
||||
templates,
|
||||
version,
|
||||
} = this.props
|
||||
const { id, name, namespace, markHubResourceAsInstalled, markHubResourceAsInstalling, templates, version } =
|
||||
this.props
|
||||
const { isTemplateInstalled } = this.state
|
||||
const resourceParams = await form({
|
||||
defaultValue: {
|
||||
@ -227,9 +220,13 @@ export default decorate([
|
||||
isTemplateInstalledOnAllPools: ({ installedTemplates }, { pools }) =>
|
||||
installedTemplates.length > 0 &&
|
||||
pools.every(pool => installedTemplates.find(template => template.$pool === pool.id) !== undefined),
|
||||
isTemplateInstalled: ({ installedTemplates }) => pool =>
|
||||
isTemplateInstalled:
|
||||
({ installedTemplates }) =>
|
||||
pool =>
|
||||
installedTemplates.find(template => template.$pool === pool.id) === undefined,
|
||||
isPoolCreated: ({ installedTemplates }) => pool =>
|
||||
isPoolCreated:
|
||||
({ installedTemplates }) =>
|
||||
pool =>
|
||||
installedTemplates.find(template => template.$pool === pool.id) !== undefined,
|
||||
},
|
||||
}),
|
||||
|
@ -307,7 +307,9 @@ export default decorate([
|
||||
this.state._status = status
|
||||
this.state.page = 1
|
||||
},
|
||||
restartVmJob: (_, params) => async (_, { log: { scheduleId, jobId } }) => {
|
||||
restartVmJob:
|
||||
(_, params) =>
|
||||
async (_, { log: { scheduleId, jobId } }) => {
|
||||
await runBackupNgJob({
|
||||
force: get(() => params.force),
|
||||
id: jobId,
|
||||
@ -361,7 +363,10 @@ export default decorate([
|
||||
const start = (page - 1) * ITEMS_PER_PAGE
|
||||
return tasksFilteredByStatus.slice(start, start + ITEMS_PER_PAGE)
|
||||
},
|
||||
optionRenderer: ({ countByStatus }) => ({ label, value }) => (
|
||||
optionRenderer:
|
||||
({ countByStatus }) =>
|
||||
({ label, value }) =>
|
||||
(
|
||||
<span>
|
||||
{_(label)} ({countByStatus[value] || 0})
|
||||
</span>
|
||||
|
@ -27,9 +27,13 @@ export default decorate([
|
||||
})),
|
||||
provideState({
|
||||
effects: {
|
||||
_downloadLog: () => ({ formattedLog }, { log }) =>
|
||||
_downloadLog:
|
||||
() =>
|
||||
({ formattedLog }, { log }) =>
|
||||
downloadLog({ log: formattedLog, date: log.start, type: 'backup NG' }),
|
||||
restartFailedVms: (_, params) => async (_, { log: { jobId: id, scheduleId: schedule, tasks, infos } }) => {
|
||||
restartFailedVms:
|
||||
(_, params) =>
|
||||
async (_, { log: { jobId: id, scheduleId: schedule, tasks, infos } }) => {
|
||||
let vms
|
||||
if (tasks !== undefined) {
|
||||
const scheduledVms = get(() => infos.find(({ message }) => message === 'vms').data.vms)
|
||||
|
@ -260,7 +260,7 @@ export default class NewVm extends BaseComponent {
|
||||
() => this.props.resourceSets,
|
||||
createSelector(
|
||||
() => this.props.location.query.resourceSet,
|
||||
resourceSetId => resourceSet => (resourceSet !== undefined ? resourceSetId === resourceSet.id : undefined)
|
||||
resourceSetId => resourceSet => resourceSet !== undefined ? resourceSetId === resourceSet.id : undefined
|
||||
)
|
||||
)
|
||||
|
||||
@ -582,7 +582,9 @@ export default class NewVm extends BaseComponent {
|
||||
const { pool } = this.props
|
||||
return pool && pool.id
|
||||
},
|
||||
poolId => ({ $pool }) => $pool === poolId
|
||||
poolId =>
|
||||
({ $pool }) =>
|
||||
$pool === poolId
|
||||
)
|
||||
_getIsInResourceSet = createSelector(
|
||||
() => {
|
||||
@ -592,8 +594,10 @@ export default class NewVm extends BaseComponent {
|
||||
objectsIds => id => includes(objectsIds, id)
|
||||
)
|
||||
|
||||
_getVmPredicate = createSelector(this._getIsInPool, this._getIsInResourceSet, (isInPool, isInResourceSet) => vm =>
|
||||
isInResourceSet(vm.id) || isInPool(vm)
|
||||
_getVmPredicate = createSelector(
|
||||
this._getIsInPool,
|
||||
this._getIsInResourceSet,
|
||||
(isInPool, isInResourceSet) => vm => isInResourceSet(vm.id) || isInPool(vm)
|
||||
)
|
||||
_getSrPredicate = createSelector(
|
||||
this._getIsInPool,
|
||||
|
@ -102,7 +102,10 @@ const NewNetwork = decorate([
|
||||
initialize: async () => ({ bondModes: await getBondModes() }),
|
||||
linkState,
|
||||
onChangeMode: (_, bondMode) => ({ bondMode }),
|
||||
onChangePif: (_, value) => ({ bonded }) => (bonded ? { pifs: value } : { pif: value }),
|
||||
onChangePif:
|
||||
(_, value) =>
|
||||
({ bonded }) =>
|
||||
bonded ? { pifs: value } : { pif: value },
|
||||
onChangeEncapsulation(_, encapsulation) {
|
||||
return { encapsulation: encapsulation.value }
|
||||
},
|
||||
@ -144,12 +147,25 @@ const NewNetwork = decorate([
|
||||
value: mode,
|
||||
}))
|
||||
: [],
|
||||
hostPredicate: ({ networks }, { pool }) => host =>
|
||||
hostPredicate:
|
||||
({ networks }, { pool }) =>
|
||||
host =>
|
||||
host.$pool === pool.id || networks.some(({ pool }) => pool !== undefined && pool.id === host.$pool),
|
||||
pifPredicate: (_, { pool }) => pif => !pif.isBondSlave && pif.vlan === -1 && pif.$host === (pool && pool.master),
|
||||
pifPredicateSdnController: (_, { pool }) => pif => canSupportPrivateNetwork(pool, pif),
|
||||
networkPifPredicate: ({ networks }) => (pif, key) => canSupportPrivateNetwork(networks[key].pool, pif),
|
||||
networkPoolPredicate: ({ networks }, { pool: rootPool }) => (pool, index) =>
|
||||
pifPredicate:
|
||||
(_, { pool }) =>
|
||||
pif =>
|
||||
!pif.isBondSlave && pif.vlan === -1 && pif.$host === (pool && pool.master),
|
||||
pifPredicateSdnController:
|
||||
(_, { pool }) =>
|
||||
pif =>
|
||||
canSupportPrivateNetwork(pool, pif),
|
||||
networkPifPredicate:
|
||||
({ networks }) =>
|
||||
(pif, key) =>
|
||||
canSupportPrivateNetwork(networks[key].pool, pif),
|
||||
networkPoolPredicate:
|
||||
({ networks }, { pool: rootPool }) =>
|
||||
(pool, index) =>
|
||||
pool.id !== rootPool.id &&
|
||||
!networks.some(
|
||||
({ pool: networksPool = {} }, networksIndex) => pool.id === networksPool.id && index !== networksIndex
|
||||
|
@ -35,23 +35,56 @@ import TabPatches from './tab-patches'
|
||||
const getMaster = createGetObject((state, props) => getPool(state, props).master)
|
||||
|
||||
const getNetworks = createGetObjectsOfType('network')
|
||||
.filter(createSelector(getPool, ({ id }) => network => network.$pool === id))
|
||||
.filter(
|
||||
createSelector(
|
||||
getPool,
|
||||
({ id }) =>
|
||||
network =>
|
||||
network.$pool === id
|
||||
)
|
||||
)
|
||||
.sort()
|
||||
|
||||
const getPifs = createGetObjectsOfType('PIF')
|
||||
.filter(createSelector(getPool, ({ id }) => pif => pif.$pool === id))
|
||||
.filter(
|
||||
createSelector(
|
||||
getPool,
|
||||
({ id }) =>
|
||||
pif =>
|
||||
pif.$pool === id
|
||||
)
|
||||
)
|
||||
.sort()
|
||||
|
||||
const getHosts = createGetObjectsOfType('host')
|
||||
.filter(createSelector(getPool, ({ id }) => obj => obj.$pool === id))
|
||||
.filter(
|
||||
createSelector(
|
||||
getPool,
|
||||
({ id }) =>
|
||||
obj =>
|
||||
obj.$pool === id
|
||||
)
|
||||
)
|
||||
.sort()
|
||||
|
||||
const getPoolSrs = createGetObjectsOfType('SR')
|
||||
.filter(createSelector(getPool, ({ id }) => sr => sr.$pool === id))
|
||||
.filter(
|
||||
createSelector(
|
||||
getPool,
|
||||
({ id }) =>
|
||||
sr =>
|
||||
sr.$pool === id
|
||||
)
|
||||
)
|
||||
.sort()
|
||||
|
||||
const getNumberOfVms = createGetObjectsOfType('VM').count(
|
||||
createSelector(getPool, ({ id }) => obj => obj.$pool === id)
|
||||
createSelector(
|
||||
getPool,
|
||||
({ id }) =>
|
||||
obj =>
|
||||
obj.$pool === id
|
||||
)
|
||||
)
|
||||
|
||||
const getLogs = createGetObjectMessages(getPool)
|
||||
|
@ -61,7 +61,12 @@ class PoolMaster extends Component {
|
||||
.sort()
|
||||
return {
|
||||
hosts: getHosts,
|
||||
hostsByMultipathing: createGroupBy(getHosts, () => ({ multipathing }) => (multipathing ? 'enabled' : 'disabled')),
|
||||
hostsByMultipathing: createGroupBy(
|
||||
getHosts,
|
||||
() =>
|
||||
({ multipathing }) =>
|
||||
multipathing ? 'enabled' : 'disabled'
|
||||
),
|
||||
gpuGroups: createGetObjectsOfType('gpuGroup')
|
||||
.filter((_, { pool }) => ({ $pool: pool.id }))
|
||||
.sort(),
|
||||
|
@ -79,7 +79,9 @@ const Modal = decorate([
|
||||
idSelectSr: generateId,
|
||||
|
||||
isStaticMode: (state, { value }) => value.networkMode === 'static',
|
||||
srPredicate: (state, { pbds, hosts }) => sr =>
|
||||
srPredicate:
|
||||
(state, { pbds, hosts }) =>
|
||||
sr =>
|
||||
isSrWritable(sr) && sr.$PBDs.some(pbd => get(() => hosts[pbds[pbd].host].hvmCapable)),
|
||||
networkPredicate: (state, { value }) => value.sr && (network => value.sr.$pool === network.$pool),
|
||||
},
|
||||
|
@ -145,7 +145,9 @@ export default class Acls extends Component {
|
||||
_getObjectPredicate = createSelector(
|
||||
() => this.state.typeFilters,
|
||||
() => this.state.someTypeFilters,
|
||||
(typeFilters, someTypeFilters) => ({ type }) => !someTypeFilters || typeFilters[type]
|
||||
(typeFilters, someTypeFilters) =>
|
||||
({ type }) =>
|
||||
!someTypeFilters || typeFilters[type]
|
||||
)
|
||||
|
||||
_selectAll = () => {
|
||||
|
@ -62,7 +62,9 @@ export default decorate([
|
||||
provideState({
|
||||
initialState: () => initialParams,
|
||||
effects: {
|
||||
setInputValue: (_, { target: { name, value } }) => state => ({
|
||||
setInputValue:
|
||||
(_, { target: { name, value } }) =>
|
||||
state => ({
|
||||
...state,
|
||||
[name]: value,
|
||||
}),
|
||||
@ -70,18 +72,24 @@ export default decorate([
|
||||
...state,
|
||||
...initialParams,
|
||||
}),
|
||||
createCloudConfig: ({ reset }) => async ({ name, template = DEFAULT_CLOUD_CONFIG_TEMPLATE }) => {
|
||||
createCloudConfig:
|
||||
({ reset }) =>
|
||||
async ({ name, template = DEFAULT_CLOUD_CONFIG_TEMPLATE }) => {
|
||||
await createCloudConfig({ name, template })
|
||||
reset()
|
||||
},
|
||||
editCloudConfig: ({ reset }) => async ({ name, template, cloudConfigToEditId }, { cloudConfigs }) => {
|
||||
editCloudConfig:
|
||||
({ reset }) =>
|
||||
async ({ name, template, cloudConfigToEditId }, { cloudConfigs }) => {
|
||||
const oldCloudConfig = find(cloudConfigs, { id: cloudConfigToEditId })
|
||||
if (oldCloudConfig.name !== name || oldCloudConfig.template !== template) {
|
||||
await editCloudConfig(cloudConfigToEditId, { name, template })
|
||||
}
|
||||
reset()
|
||||
},
|
||||
populateForm: (_, { id, name, template }) => state => ({
|
||||
populateForm:
|
||||
(_, { id, name, template }) =>
|
||||
state => ({
|
||||
...state,
|
||||
name,
|
||||
cloudConfigToEditId: id,
|
||||
|
@ -51,7 +51,9 @@ export default decorate([
|
||||
setProxy(_, proxy) {
|
||||
this.state.proxyId = resolveId(proxy)
|
||||
},
|
||||
editRemote: ({ reset }) => state => {
|
||||
editRemote:
|
||||
({ reset }) =>
|
||||
state => {
|
||||
const {
|
||||
remote,
|
||||
domain = remote.domain || '',
|
||||
@ -88,7 +90,9 @@ export default decorate([
|
||||
proxy: proxyId,
|
||||
}).then(reset)
|
||||
},
|
||||
createRemote: ({ reset }) => async (state, { remotes }) => {
|
||||
createRemote:
|
||||
({ reset }) =>
|
||||
async (state, { remotes }) => {
|
||||
if (some(remotes, { name: state.name })) {
|
||||
return alert(
|
||||
<span>
|
||||
|
@ -210,7 +210,9 @@ export default class TabGeneral extends Component {
|
||||
}
|
||||
)
|
||||
|
||||
_getGenerateLink = createSelector(this._getDiskGroups, diskGroups => ids =>
|
||||
_getGenerateLink = createSelector(
|
||||
this._getDiskGroups,
|
||||
diskGroups => ids =>
|
||||
`#/srs/${this.props.sr.id}/disks?s=${encodeURIComponent(
|
||||
`id:|(${flattenDeep(
|
||||
map(pick(keyBy(diskGroups, 'id'), ids), ({ id, baseCopies, vdis, snapshots, type }) =>
|
||||
|
@ -79,7 +79,11 @@ const Field = ({ title, children }) => (
|
||||
})
|
||||
class Node extends Component {
|
||||
_replaceBrick = async ({ brick, vm }) => {
|
||||
const { sr, brickSize, onSameVm = false } = await confirm({
|
||||
const {
|
||||
sr,
|
||||
brickSize,
|
||||
onSameVm = false,
|
||||
} = await confirm({
|
||||
icon: 'refresh',
|
||||
title: _('xosanReplace'),
|
||||
body: <ReplaceBrickModalBody vm={vm} />,
|
||||
|
@ -87,8 +87,9 @@ const shareVmProxy = vm => shareVm(vm, vm.resourceSet)
|
||||
const getSrs = createGetObjectsOfType('SR').pick(createSelector(getVdis, vdis => uniq(map(vdis, '$SR'))))
|
||||
const getSrsContainers = createSelector(getSrs, srs => uniq(map(srs, '$container')))
|
||||
|
||||
const getAffinityHostPredicate = createSelector(getSrsContainers, containers => host =>
|
||||
every(containers, container => container === host.$pool || container === host.id)
|
||||
const getAffinityHostPredicate = createSelector(
|
||||
getSrsContainers,
|
||||
containers => host => every(containers, container => container === host.$pool || container === host.id)
|
||||
)
|
||||
|
||||
return {
|
||||
@ -275,7 +276,9 @@ class AddAclsModal extends Component {
|
||||
_getPredicate = createSelector(
|
||||
() => this.props.acls,
|
||||
() => this.props.vm,
|
||||
(acls, object) => ({ id: subject, permission }) => permission !== 'admin' && !some(acls, { object, subject })
|
||||
(acls, object) =>
|
||||
({ id: subject, permission }) =>
|
||||
permission !== 'admin' && !some(acls, { object, subject })
|
||||
)
|
||||
|
||||
render() {
|
||||
@ -306,7 +309,9 @@ const Acls = decorate([
|
||||
}),
|
||||
provideState({
|
||||
effects: {
|
||||
addAcls: () => (state, { acls, vm }) =>
|
||||
addAcls:
|
||||
() =>
|
||||
(state, { acls, vm }) =>
|
||||
confirm({
|
||||
title: _('vmAddAcls'),
|
||||
body: <AddAclsModal acls={acls} vm={vm} />,
|
||||
@ -319,7 +324,9 @@ const Acls = decorate([
|
||||
return Promise.all(map(subjects, subject => addAcl({ subject, object: vm, action })))
|
||||
})
|
||||
.catch(err => err && error(_('addAclsErrorTitle'), err.message || String(err))),
|
||||
removeAcl: (_, { currentTarget: { dataset } }) => (_, { vm: object }) =>
|
||||
removeAcl:
|
||||
(_, { currentTarget: { dataset } }) =>
|
||||
(_, { vm: object }) =>
|
||||
removeAcl({
|
||||
action: dataset.action,
|
||||
object,
|
||||
|
@ -18,7 +18,10 @@ const BackupTab = decorate([
|
||||
provideState({
|
||||
computed: {
|
||||
jobIds: ({ predicate }, { jobs }) => filter(jobs, predicate).map(_ => _.id),
|
||||
predicate: (_, { vm }) => ({ vms }) => vms !== undefined && createPredicate(omit(vms, 'power_state'))(vm),
|
||||
predicate:
|
||||
(_, { vm }) =>
|
||||
({ vms }) =>
|
||||
vms !== undefined && createPredicate(omit(vms, 'power_state'))(vm),
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
|
@ -565,7 +565,9 @@ export default class TabDisks extends Component {
|
||||
}
|
||||
)
|
||||
|
||||
_getCheckSr = createSelector(this._getRequiredHost, requiredHost => sr =>
|
||||
_getCheckSr = createSelector(
|
||||
this._getRequiredHost,
|
||||
requiredHost => sr =>
|
||||
sr === undefined || isSrShared(sr) || requiredHost === undefined || sr.$container === requiredHost
|
||||
)
|
||||
|
||||
@ -597,7 +599,9 @@ export default class TabDisks extends Component {
|
||||
)
|
||||
)
|
||||
|
||||
_getGenerateWarningBeforeMigrate = createSelector(this._getCheckSr, check => sr =>
|
||||
_getGenerateWarningBeforeMigrate = createSelector(
|
||||
this._getCheckSr,
|
||||
check => sr =>
|
||||
check(sr) ? null : (
|
||||
<span className='text-warning'>
|
||||
<Icon icon='alarm' /> {_('warningVdiSr')}
|
||||
|
@ -129,9 +129,10 @@ export default class Xosan extends Component {
|
||||
}
|
||||
)
|
||||
|
||||
_getAvailableLicenses = createFilter(() => this.props.xosanLicenses, [
|
||||
({ boundObjectId, expires }) => boundObjectId === undefined && (expires === undefined || expires > Date.now()),
|
||||
])
|
||||
_getAvailableLicenses = createFilter(
|
||||
() => this.props.xosanLicenses,
|
||||
[({ boundObjectId, expires }) => boundObjectId === undefined && (expires === undefined || expires > Date.now())]
|
||||
)
|
||||
|
||||
_getKnownXosans = createSelector(
|
||||
createSelector(
|
||||
|
@ -237,8 +237,10 @@ const XOSAN_INDIVIDUAL_ACTIONS = [
|
||||
return hostsNeedRestartByPool
|
||||
})
|
||||
|
||||
const getPoolPredicate = createSelector(getXosanSrs, getHosts, (srs, hosts) => pool =>
|
||||
hosts[pool.master].productBrand !== 'XCP-ng' && every(srs, sr => sr.$pool !== pool.id)
|
||||
const getPoolPredicate = createSelector(
|
||||
getXosanSrs,
|
||||
getHosts,
|
||||
(srs, hosts) => pool => hosts[pool.master].productBrand !== 'XCP-ng' && every(srs, sr => sr.$pool !== pool.id)
|
||||
)
|
||||
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user