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