parent
46100729b0
commit
fb1bf6a1e7
@ -52,12 +52,13 @@ export default class NfsHandler extends LocalHandler {
|
||||
|
||||
async _mount () {
|
||||
await fs.ensureDir(this._getRealPath())
|
||||
const { host, path, port } = this._remote
|
||||
return execa('mount', [
|
||||
'-t',
|
||||
'nfs',
|
||||
'-o',
|
||||
'vers=3',
|
||||
`${this._remote.host}:${this._remote.path}`,
|
||||
`${host}${port !== undefined ? ':' + port : ''}:${path}`,
|
||||
this._getRealPath(),
|
||||
])
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
- [Backup NG form] Add a link to the remotes' settings [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) [#3106](https://github.com/vatesfr/xen-orchestra/issues/3106) [#2299](https://github.com/vatesfr/xen-orchestra/issues/2299) (PR [#3128](https://github.com/vatesfr/xen-orchestra/pull/3128))
|
||||
- [Backup NG logs] Make copy to clipboard and report buttons always available [#3130](https://github.com/vatesfr/xen-orchestra/issues/3130) (PR [#3133](https://github.com/vatesfr/xen-orchestra/pull/3133))
|
||||
- Warning message when creating a local remote [#3105](https://github.com/vatesfr/xen-orchestra/issues/3105) (PR [3142](https://github.com/vatesfr/xen-orchestra/pull/3142))
|
||||
- [Remotes] Allow optional port for NFS remote [2299](https://github.com/vatesfr/xen-orchestra/issues/2299) (PR [#3131](https://github.com/vatesfr/xen-orchestra/pull/3131))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@ -15,6 +16,8 @@
|
||||
|
||||
### Released packages
|
||||
|
||||
- xo-remote-parser 0.4.0
|
||||
- @xen-orchestra/fs 0.2.0
|
||||
- vhd-lib 0.3.0
|
||||
- vhd-cli 0.1.0
|
||||
- xo-server v5.22.0
|
||||
|
@ -3,6 +3,8 @@ import map from 'lodash/map'
|
||||
import trim from 'lodash/trim'
|
||||
import trimStart from 'lodash/trimStart'
|
||||
|
||||
const NFS_RE = /^([^:]+):(?:(\d+):)?([^:]+)$/
|
||||
|
||||
const sanitizePath = (...paths) =>
|
||||
filter(map(paths, s => s && filter(map(s.split('/'), trim)).join('/'))).join(
|
||||
'/'
|
||||
@ -17,8 +19,9 @@ export const parse = string => {
|
||||
object.path = `/${trimStart(rest, '/')}` // the leading slash has been forgotten on client side first implementation
|
||||
} else if (type === 'nfs') {
|
||||
object.type = 'nfs'
|
||||
const [host, path] = rest.split(':')
|
||||
const [, host, port, path] = NFS_RE.exec(rest)
|
||||
object.host = host
|
||||
object.port = port
|
||||
object.path = `/${trimStart(path, '/')}` // takes care of a missing leading slash coming from previous version format
|
||||
} else if (type === 'smb') {
|
||||
object.type = 'smb'
|
||||
@ -39,11 +42,19 @@ export const parse = string => {
|
||||
return object
|
||||
}
|
||||
|
||||
export const format = ({ type, host, path, username, password, domain }) => {
|
||||
export const format = ({
|
||||
type,
|
||||
host,
|
||||
path,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
domain,
|
||||
}) => {
|
||||
type === 'local' && (type = 'file')
|
||||
let string = `${type}://`
|
||||
if (type === 'nfs') {
|
||||
string += `${host}:`
|
||||
string += `${host}:${port !== undefined ? port + ':' : ''}`
|
||||
}
|
||||
if (type === 'smb') {
|
||||
string += `${username}:${password}@${domain}\\\\${host}`
|
||||
|
@ -31,6 +31,16 @@ const data = deepFreeze({
|
||||
object: {
|
||||
type: 'nfs',
|
||||
host: '192.168.100.225',
|
||||
port: undefined,
|
||||
path: '/media/nfs',
|
||||
},
|
||||
},
|
||||
'nfs with port': {
|
||||
string: 'nfs://192.168.100.225:20:/media/nfs',
|
||||
object: {
|
||||
type: 'nfs',
|
||||
host: '192.168.100.225',
|
||||
port: '20',
|
||||
path: '/media/nfs',
|
||||
},
|
||||
},
|
||||
|
@ -16,6 +16,7 @@ import getEventValue from '../get-event-value'
|
||||
import propTypes from '../prop-types-decorator'
|
||||
import { formatSizeRaw, parseSize } from '../utils'
|
||||
|
||||
export Number from './number'
|
||||
export Select from './select'
|
||||
|
||||
// ===================================================================
|
||||
|
@ -471,6 +471,7 @@ const messages = {
|
||||
remoteMyNamePlaceHolder: 'Name *',
|
||||
remoteLocalPlaceHolderPath: '/path/to/backup',
|
||||
remoteNfsPlaceHolderHost: 'host *',
|
||||
remoteNfsPlaceHolderPort: 'Port',
|
||||
remoteNfsPlaceHolderPath: 'path/to/backup',
|
||||
remoteSmbPlaceHolderRemotePath: 'subfolder [path\\\\to\\\\backup]',
|
||||
remoteSmbPlaceHolderUsername: 'Username',
|
||||
|
@ -18,7 +18,7 @@ import { constructSmartPattern, destructSmartPattern } from 'smart-backup'
|
||||
import { Container, Col, Row } from 'grid'
|
||||
import { injectState, provideState } from '@julien-f/freactal'
|
||||
import { SelectRemote, SelectSr, SelectVm } from 'select-objects'
|
||||
import { Toggle } from 'form'
|
||||
import { Number, Toggle } from 'form'
|
||||
import {
|
||||
cloneDeep,
|
||||
flatten,
|
||||
@ -42,7 +42,7 @@ import {
|
||||
|
||||
import Schedules from './schedules'
|
||||
import SmartBackup from './smart-backup'
|
||||
import { FormFeedback, FormGroup, Input, Number, Ul, Li } from './utils'
|
||||
import { FormFeedback, FormGroup, Input, Ul, Li } from './utils'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
|
@ -7,8 +7,9 @@ import { Card, CardBlock } from 'card'
|
||||
import { generateRandomId } from 'utils'
|
||||
import { injectState, provideState } from '@julien-f/freactal'
|
||||
import { isEqual } from 'lodash'
|
||||
import { Number } from 'form'
|
||||
|
||||
import { FormFeedback, FormGroup, Number } from './utils'
|
||||
import { FormFeedback, FormGroup } from './utils'
|
||||
|
||||
export default [
|
||||
injectState,
|
||||
|
@ -1,46 +1,12 @@
|
||||
import Icon from 'icon'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { injectState, provideState } from '@julien-f/freactal'
|
||||
|
||||
export const FormGroup = props => <div {...props} className='form-group' />
|
||||
export const Input = props => <input {...props} className='form-control' />
|
||||
export const Ul = props => <ul {...props} className='list-group' />
|
||||
export const Li = props => <li {...props} className='list-group-item' />
|
||||
|
||||
export const Number = [
|
||||
provideState({
|
||||
effects: {
|
||||
onChange: (_, { target: { value } }) => (state, props) => {
|
||||
if (value === '') {
|
||||
if (!props.optional) {
|
||||
return
|
||||
}
|
||||
|
||||
props.onChange(undefined)
|
||||
return
|
||||
}
|
||||
props.onChange(+value)
|
||||
},
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
({ effects, state, value, optional }) => (
|
||||
<Input
|
||||
type='number'
|
||||
onChange={effects.onChange}
|
||||
value={value === undefined ? undefined : String(value)}
|
||||
min='0'
|
||||
/>
|
||||
),
|
||||
].reduceRight((value, decorator) => decorator(value))
|
||||
|
||||
Number.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.number.isRequired,
|
||||
optional: PropTypes.bool,
|
||||
}
|
||||
|
||||
export const FormFeedback = ({
|
||||
component: Component,
|
||||
error,
|
||||
|
@ -14,8 +14,9 @@ import { addSubscriptions } from 'utils'
|
||||
import { alert, confirm } from 'modal'
|
||||
import { error } from 'notification'
|
||||
import { format, parse } from 'xo-remote-parser'
|
||||
import { Password, Text } from 'editable'
|
||||
import { injectIntl } from 'react-intl'
|
||||
import { Number, Password, Text } from 'editable'
|
||||
import { Number as InputNumber } from 'form'
|
||||
|
||||
import {
|
||||
createRemote,
|
||||
@ -34,7 +35,9 @@ const remoteTypes = {
|
||||
smb: 'remoteTypeSmb',
|
||||
}
|
||||
const _changeUrlElement = (remote, value, element) =>
|
||||
editRemote(remote, { url: format({ ...remote, [element]: value }) })
|
||||
editRemote(remote, {
|
||||
url: format({ ...remote, [element]: value === null ? undefined : value }),
|
||||
})
|
||||
const _showError = remote => alert(_('remoteConnectionFailed'), remote.error)
|
||||
const COLUMN_NAME = {
|
||||
component: @injectIntl
|
||||
@ -122,6 +125,15 @@ const COLUMNS_NFS_REMOTE = [
|
||||
value={remote.host}
|
||||
/>
|
||||
:
|
||||
<Number
|
||||
nullable
|
||||
onChange={v => _changeUrlElement(remote, v, 'port')}
|
||||
placeholder={intl.formatMessage(
|
||||
messages.remoteNfsPlaceHolderPort
|
||||
)}
|
||||
value={remote.port || ''}
|
||||
/>
|
||||
:
|
||||
<Text
|
||||
onChange={v => _changeUrlElement(remote, v, 'path')}
|
||||
placeholder={intl.formatMessage(
|
||||
@ -278,6 +290,7 @@ export default class Remotes extends Component {
|
||||
name: '',
|
||||
password: '',
|
||||
path: '',
|
||||
port: '',
|
||||
type: 'nfs',
|
||||
username: '',
|
||||
}
|
||||
@ -294,12 +307,22 @@ export default class Remotes extends Component {
|
||||
: this._createRemote()
|
||||
|
||||
_createRemote = async () => {
|
||||
const { type, name, host, path, username, password, domain } = this.state
|
||||
const {
|
||||
domain,
|
||||
host,
|
||||
name,
|
||||
password,
|
||||
path,
|
||||
port,
|
||||
type,
|
||||
username,
|
||||
} = this.state
|
||||
|
||||
const urlParams = {
|
||||
type,
|
||||
host,
|
||||
path,
|
||||
port,
|
||||
type,
|
||||
}
|
||||
username && (urlParams.username = username)
|
||||
password && (urlParams.password = password)
|
||||
@ -321,6 +344,7 @@ export default class Remotes extends Component {
|
||||
name: '',
|
||||
password: '',
|
||||
path: '',
|
||||
port: '',
|
||||
type: 'nfs',
|
||||
username: '',
|
||||
})
|
||||
@ -331,7 +355,16 @@ export default class Remotes extends Component {
|
||||
|
||||
render () {
|
||||
const { remotes = {} } = this.props
|
||||
const { type, name, host, path, username, password, domain } = this.state
|
||||
const {
|
||||
domain,
|
||||
host,
|
||||
name,
|
||||
password,
|
||||
path,
|
||||
port,
|
||||
type,
|
||||
username,
|
||||
} = this.state
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -440,6 +473,15 @@ export default class Remotes extends Component {
|
||||
value={host}
|
||||
required
|
||||
/>
|
||||
<br />
|
||||
<InputNumber
|
||||
onChange={this.linkState('port')}
|
||||
optional
|
||||
placeholder={this.props.intl.formatMessage(
|
||||
messages.remoteNfsPlaceHolderPort
|
||||
)}
|
||||
value={port}
|
||||
/>
|
||||
</div>
|
||||
<div className='input-group form-group'>
|
||||
<span className='input-group-addon'>/</span>
|
||||
|
Loading…
Reference in New Issue
Block a user