parent
e6deb29070
commit
3cef668a75
@ -241,7 +241,7 @@ export async function createHba ({ host, nameLabel, nameDescription, scsiId }) {
|
|||||||
const xapi = this.getXapi(host)
|
const xapi = this.getXapi(host)
|
||||||
|
|
||||||
const deviceConfig = {
|
const deviceConfig = {
|
||||||
scsiId,
|
SCSIid: scsiId,
|
||||||
}
|
}
|
||||||
|
|
||||||
const srRef = await xapi.call(
|
const srRef = await xapi.call(
|
||||||
@ -251,7 +251,7 @@ export async function createHba ({ host, nameLabel, nameDescription, scsiId }) {
|
|||||||
'0',
|
'0',
|
||||||
nameLabel,
|
nameLabel,
|
||||||
nameDescription,
|
nameDescription,
|
||||||
'lvmoohba', // SR LVM over HBA
|
'lvmohba', // SR LVM over HBA
|
||||||
'user', // recommended by Citrix
|
'user', // recommended by Citrix
|
||||||
true,
|
true,
|
||||||
{}
|
{}
|
||||||
@ -366,7 +366,7 @@ export async function probeHba ({ host }) {
|
|||||||
let xml
|
let xml
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await xapi.call('SR.probe', host._xapiRef, 'type', {})
|
await xapi.call('SR.probe', host._xapiRef, {}, 'lvmohba', {})
|
||||||
|
|
||||||
throw new Error('the call above should have thrown an error')
|
throw new Error('the call above should have thrown an error')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -382,7 +382,7 @@ export async function probeHba ({ host }) {
|
|||||||
hbaDevices.push({
|
hbaDevices.push({
|
||||||
hba: hbaDevice.hba.trim(),
|
hba: hbaDevice.hba.trim(),
|
||||||
path: hbaDevice.path.trim(),
|
path: hbaDevice.path.trim(),
|
||||||
scsciId: hbaDevice.SCSIid.trim(),
|
scsiId: hbaDevice.SCSIid.trim(),
|
||||||
size: hbaDevice.size.trim(),
|
size: hbaDevice.size.trim(),
|
||||||
vendor: hbaDevice.vendor.trim(),
|
vendor: hbaDevice.vendor.trim(),
|
||||||
})
|
})
|
||||||
@ -668,6 +668,34 @@ probeIscsiExists.resolve = {
|
|||||||
host: ['host', 'host', 'administrate'],
|
host: ['host', 'host', 'administrate'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// This function helps to detect if this HBA already exists in XAPI
|
||||||
|
// It returns a table of SR UUID, empty if no existing connections
|
||||||
|
|
||||||
|
export async function probeHbaExists ({ host, scsiId }) {
|
||||||
|
const xapi = this.getXapi(host)
|
||||||
|
|
||||||
|
const deviceConfig = {
|
||||||
|
SCSIid: scsiId,
|
||||||
|
}
|
||||||
|
|
||||||
|
const xml = parseXml(
|
||||||
|
await xapi.call('SR.probe', host._xapiRef, deviceConfig, 'lvmohba', {})
|
||||||
|
)
|
||||||
|
|
||||||
|
// get the UUID of SR connected to this LUN
|
||||||
|
return ensureArray(xml.SRlist.SR).map(sr => ({ uuid: sr.UUID.trim() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
probeHbaExists.params = {
|
||||||
|
host: { type: 'string' },
|
||||||
|
scsiId: { type: 'string' },
|
||||||
|
}
|
||||||
|
|
||||||
|
probeHbaExists.resolve = {
|
||||||
|
host: ['host', 'host', 'administrate'],
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// This function helps to detect if this NFS SR already exists in XAPI
|
// This function helps to detect if this NFS SR already exists in XAPI
|
||||||
// It returns a table of SR UUID, empty if no existing connections
|
// It returns a table of SR UUID, empty if no existing connections
|
||||||
|
@ -429,6 +429,7 @@ const messages = {
|
|||||||
newSrPath: 'Path',
|
newSrPath: 'Path',
|
||||||
newSrIqn: 'IQN',
|
newSrIqn: 'IQN',
|
||||||
newSrLun: 'LUN',
|
newSrLun: 'LUN',
|
||||||
|
newSrNoHba: 'No HBA devices',
|
||||||
newSrAuth: 'with auth.',
|
newSrAuth: 'with auth.',
|
||||||
newSrUsername: 'User Name',
|
newSrUsername: 'User Name',
|
||||||
newSrPassword: 'Password',
|
newSrPassword: 'Password',
|
||||||
|
@ -1948,6 +1948,11 @@ export const probeSrIscsiExists = (
|
|||||||
return _call('sr.probeIscsiExists', params)
|
return _call('sr.probeIscsiExists', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const probeSrHba = host => _call('sr.probeHba', { host })
|
||||||
|
|
||||||
|
export const probeSrHbaExists = (host, scsiId) =>
|
||||||
|
_call('sr.probeHbaExists', { host, scsiId })
|
||||||
|
|
||||||
export const reattachSr = (host, uuid, nameLabel, nameDescription, type) =>
|
export const reattachSr = (host, uuid, nameLabel, nameDescription, type) =>
|
||||||
_call('sr.reattach', { host, uuid, nameLabel, nameDescription, type })
|
_call('sr.reattach', { host, uuid, nameLabel, nameDescription, type })
|
||||||
|
|
||||||
@ -1985,6 +1990,9 @@ export const createSrIscsi = (
|
|||||||
return _call('sr.createIscsi', params)
|
return _call('sr.createIscsi', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createSrHba = (host, nameLabel, nameDescription, scsiId) =>
|
||||||
|
_call('sr.createHba', { host, nameLabel, nameDescription, scsiId })
|
||||||
|
|
||||||
export const createSrIso = (
|
export const createSrIso = (
|
||||||
host,
|
host,
|
||||||
nameLabel,
|
nameLabel,
|
||||||
|
@ -16,6 +16,7 @@ import Wizard, { Section } from 'wizard'
|
|||||||
import { confirm } from 'modal'
|
import { confirm } from 'modal'
|
||||||
import { connectStore, formatSize } from 'utils'
|
import { connectStore, formatSize } from 'utils'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
|
import { ignoreErrors } from 'promise-toolbox'
|
||||||
import { injectIntl } from 'react-intl'
|
import { injectIntl } from 'react-intl'
|
||||||
import { Password, Select } from 'form'
|
import { Password, Select } from 'form'
|
||||||
import { SelectHost } from 'select-objects'
|
import { SelectHost } from 'select-objects'
|
||||||
@ -30,11 +31,14 @@ import {
|
|||||||
createSrIscsi,
|
createSrIscsi,
|
||||||
createSrLvm,
|
createSrLvm,
|
||||||
createSrNfs,
|
createSrNfs,
|
||||||
|
createSrHba,
|
||||||
probeSrIscsiExists,
|
probeSrIscsiExists,
|
||||||
probeSrIscsiIqns,
|
probeSrIscsiIqns,
|
||||||
probeSrIscsiLuns,
|
probeSrIscsiLuns,
|
||||||
probeSrNfs,
|
probeSrNfs,
|
||||||
probeSrNfsExists,
|
probeSrNfsExists,
|
||||||
|
probeSrHba,
|
||||||
|
probeSrHbaExists,
|
||||||
reattachSrIso,
|
reattachSrIso,
|
||||||
reattachSr,
|
reattachSr,
|
||||||
} from 'xo'
|
} from 'xo'
|
||||||
@ -45,6 +49,50 @@ import {
|
|||||||
onChange: propTypes.func.isRequired,
|
onChange: propTypes.func.isRequired,
|
||||||
options: propTypes.array.isRequired,
|
options: propTypes.array.isRequired,
|
||||||
})
|
})
|
||||||
|
class SelectScsiId extends Component {
|
||||||
|
_getOptions = createSelector(
|
||||||
|
() => this.props.options,
|
||||||
|
options =>
|
||||||
|
map(options, ({ vendor, path, size, scsiId }) => ({
|
||||||
|
label: `${vendor} - ${path} (${formatSize(size)})`,
|
||||||
|
value: scsiId,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
_handleChange = opt => {
|
||||||
|
const { props } = this
|
||||||
|
|
||||||
|
this.setState({ value: opt.value }, () => props.onChange(opt.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
return this.componentDidUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate () {
|
||||||
|
let options
|
||||||
|
if (
|
||||||
|
this.state.value === null &&
|
||||||
|
(options = this._getOptions()).length === 1
|
||||||
|
) {
|
||||||
|
this._handleChange(options[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = { value: null }
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
clearable={false}
|
||||||
|
onChange={this._handleChange}
|
||||||
|
options={this._getOptions()}
|
||||||
|
value={this.state.value}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SelectIqn extends Component {
|
class SelectIqn extends Component {
|
||||||
_getOptions = createSelector(
|
_getOptions = createSelector(
|
||||||
() => this.props.options,
|
() => this.props.options,
|
||||||
@ -141,10 +189,11 @@ class SelectLun extends Component {
|
|||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
const SR_TYPE_TO_LABEL = {
|
const SR_TYPE_TO_LABEL = {
|
||||||
nfs: 'NFS',
|
hba: 'HBA',
|
||||||
iscsi: 'iSCSI',
|
iscsi: 'iSCSI',
|
||||||
lvm: 'Local LVM',
|
|
||||||
local: 'Local',
|
local: 'Local',
|
||||||
|
lvm: 'Local LVM',
|
||||||
|
nfs: 'NFS',
|
||||||
nfsiso: 'NFS ISO',
|
nfsiso: 'NFS ISO',
|
||||||
smb: 'SMB',
|
smb: 'SMB',
|
||||||
}
|
}
|
||||||
@ -155,7 +204,7 @@ const SR_GROUP_TO_LABEL = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const typeGroups = {
|
const typeGroups = {
|
||||||
vdisr: ['nfs', 'iscsi', 'lvm'],
|
vdisr: ['hba', 'iscsi', 'lvm', 'nfs'],
|
||||||
isosr: ['local', 'nfsiso', 'smb'],
|
isosr: ['local', 'nfsiso', 'smb'],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +232,7 @@ export default class New extends Component {
|
|||||||
lockCreation: undefined,
|
lockCreation: undefined,
|
||||||
lun: undefined,
|
lun: undefined,
|
||||||
luns: undefined,
|
luns: undefined,
|
||||||
|
hbaDevices: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
path: undefined,
|
path: undefined,
|
||||||
paths: undefined,
|
paths: undefined,
|
||||||
@ -212,7 +262,7 @@ export default class New extends Component {
|
|||||||
server,
|
server,
|
||||||
username,
|
username,
|
||||||
} = this.refs
|
} = this.refs
|
||||||
const { host, iqn, lun, path, type } = this.state
|
const { host, iqn, lun, path, type, scsiId } = this.state
|
||||||
|
|
||||||
const createMethodFactories = {
|
const createMethodFactories = {
|
||||||
nfs: async () => {
|
nfs: async () => {
|
||||||
@ -235,6 +285,20 @@ export default class New extends Component {
|
|||||||
path
|
path
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
hba: async () => {
|
||||||
|
const previous = await probeSrHbaExists(host.id, scsiId)
|
||||||
|
if (previous && previous.length > 0) {
|
||||||
|
try {
|
||||||
|
await confirm({
|
||||||
|
title: _('existingLunModalTitle'),
|
||||||
|
body: <p>{_('existingLunModalText')}</p>,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return createSrHba(host.id, name.value, description.value, scsiId)
|
||||||
|
},
|
||||||
iscsi: async () => {
|
iscsi: async () => {
|
||||||
const previous = await probeSrIscsiExists(
|
const previous = await probeSrIscsiExists(
|
||||||
host.id,
|
host.id,
|
||||||
@ -311,16 +375,32 @@ export default class New extends Component {
|
|||||||
_handleDescriptionChange = event =>
|
_handleDescriptionChange = event =>
|
||||||
this.setState({ description: event.target.value })
|
this.setState({ description: event.target.value })
|
||||||
|
|
||||||
_handleSrTypeSelection = event => {
|
_handleSrTypeSelection = async event => {
|
||||||
const type = event.target.value
|
const type = event.target.value
|
||||||
this.setState({
|
this.setState({
|
||||||
type,
|
hbaDevices: undefined,
|
||||||
paths: undefined,
|
|
||||||
iqns: undefined,
|
iqns: undefined,
|
||||||
|
paths: undefined,
|
||||||
|
summary: includes(['lvm', 'local', 'smb', 'hba']),
|
||||||
|
type,
|
||||||
|
unused: undefined,
|
||||||
usage: undefined,
|
usage: undefined,
|
||||||
used: undefined,
|
used: undefined,
|
||||||
unused: undefined,
|
})
|
||||||
summary: type === 'lvm' || type === 'local' || type === 'smb',
|
if (type === 'hba' && this.state.host !== undefined) {
|
||||||
|
this.setState(({ loading }) => ({ loading: loading + 1 }))
|
||||||
|
const hbaDevices = await probeSrHba(this.state.host.id)::ignoreErrors()
|
||||||
|
this.setState(({ loading }) => ({
|
||||||
|
hbaDevices,
|
||||||
|
loading: loading - 1,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleSrHbaSelection = async scsiId => {
|
||||||
|
this.setState({
|
||||||
|
scsiId,
|
||||||
|
usage: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,6 +564,7 @@ export default class New extends Component {
|
|||||||
auth,
|
auth,
|
||||||
host,
|
host,
|
||||||
iqns,
|
iqns,
|
||||||
|
hbaDevices,
|
||||||
loading,
|
loading,
|
||||||
lockCreation,
|
lockCreation,
|
||||||
lun,
|
lun,
|
||||||
@ -579,6 +660,21 @@ export default class New extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
)}
|
)}
|
||||||
|
{type === 'hba' && (
|
||||||
|
<fieldset>
|
||||||
|
<label>{_('newSrLun')}</label>
|
||||||
|
<div>
|
||||||
|
{!isEmpty(hbaDevices) ? (
|
||||||
|
<SelectScsiId
|
||||||
|
options={hbaDevices}
|
||||||
|
onChange={this._handleSrHbaSelection}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<em>{_('newSrNoHba')}</em>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
)}
|
||||||
{paths && (
|
{paths && (
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label htmlFor='selectSrPath'>{_('newSrPath')}</label>
|
<label htmlFor='selectSrPath'>{_('newSrPath')}</label>
|
||||||
@ -774,8 +870,8 @@ export default class New extends Component {
|
|||||||
<p key={key}>
|
<p key={key}>
|
||||||
{sr.uuid}
|
{sr.uuid}
|
||||||
<span className='pull-right'>
|
<span className='pull-right'>
|
||||||
<a className='btn btn-warning'>{_('newSrInUse')}</a> //
|
{/* FIXME Goes to sr view */}
|
||||||
FIXME Goes to sr view
|
<a className='btn btn-warning'>{_('newSrInUse')}</a>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
@ -801,7 +897,7 @@ export default class New extends Component {
|
|||||||
<dd>{formatSize(+lun.size)}</dd>
|
<dd>{formatSize(+lun.size)}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
)}
|
)}
|
||||||
{type === 'nfs' && (
|
{includes(['nfs', 'hba'], type) && (
|
||||||
<dl className='dl-horizontal'>
|
<dl className='dl-horizontal'>
|
||||||
<dt>{_('newSrPath')}</dt>
|
<dt>{_('newSrPath')}</dt>
|
||||||
<dd>{path}</dd>
|
<dd>{path}</dd>
|
||||||
|
Loading…
Reference in New Issue
Block a user