Skip to content

Commit d06bfb0

Browse files
JasperB-TeamBluedupondje
authored andcommitted
ui: ability to search for targets on storage connections pop-up screen
Similar to the manage storage domain screen, one can now search for storage connections based on port, ip and host. Disables searching and adding when storage domain is not in maintenance due to this not being supported at the moment. Only show hosts that are up in dropdown menu so non usable hosts do not show up. Signed-off-by: Jasper Berton <jasper.berton@team.blue>
1 parent 03c8b12 commit d06bfb0

8 files changed

Lines changed: 314 additions & 62 deletions

File tree

src/components/helper/DataProviderHook.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState, useMemo, useRef } from 'react'
1+
import { useEffect, useState, useMemo, useRef, useCallback } from 'react'
22

33
export function useDataProvider ({ fetchData, parameters = [], trigger, enabled = true, debugState = false }) {
44
const [{ data, fetchError, fetchInProgress }, setState] = useState({
@@ -8,9 +8,9 @@ export function useDataProvider ({ fetchData, parameters = [], trigger, enabled
88
})
99
const prevParams = useRef(null)
1010
const prevTrigger = useRef(null)
11-
const params = useMemo(() => ([...parameters]), [...parameters])
11+
const params = useMemo(() => ([...parameters]), [parameters])
1212

13-
const debug = (msg) => {
13+
const debug = useCallback((msg) => {
1414
if (!debugState) {
1515
return
1616
}
@@ -23,21 +23,22 @@ export function useDataProvider ({ fetchData, parameters = [], trigger, enabled
2323
fetchInProgress,
2424
}
2525
console.log(`${msg} in state: ${JSON.stringify(state)}`)
26-
}
26+
}, [data, debugState, fetchData, fetchError, fetchInProgress, parameters.length, trigger])
2727

2828
debug('render')
2929

3030
useEffect(() => {
3131
if (fetchInProgress || !enabled) {
3232
return
3333
}
34+
const paramsKey = JSON.stringify(params)
3435

35-
if (data && !fetchError && prevParams.current === params) {
36+
if (data && !fetchError && prevParams.current === paramsKey) {
3637
// re-fetch when params changed
3738
return
3839
}
3940

40-
if (fetchError && prevParams.current === params && prevTrigger.current === trigger) {
41+
if (fetchError && prevParams.current === paramsKey && prevTrigger.current === trigger) {
4142
// re-fetch when params or trigger changed
4243
return
4344
}
@@ -47,7 +48,7 @@ export function useDataProvider ({ fetchData, parameters = [], trigger, enabled
4748
fetchError: false,
4849
fetchInProgress: true,
4950
})
50-
prevParams.current = params
51+
prevParams.current = paramsKey
5152
prevTrigger.current = trigger
5253
debug('start')
5354
fetchData(...params)
@@ -67,7 +68,7 @@ export function useDataProvider ({ fetchData, parameters = [], trigger, enabled
6768
fetchInProgress: false,
6869
})
6970
})
70-
})
71+
}, [fetchInProgress, enabled, data, fetchError, params, trigger, debug, fetchData])
7172

7273
return useMemo(() => ({
7374
data,

src/intl/locales/en-US.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@
170170
"storage.domains.connections.table.connection.attached": "ATTACHED",
171171
"storage.domains.connections.connection.remove.button": "Remove",
172172
"storage.domains.connections.connection.add.button": "Add",
173+
"storage.domains.connections.discover.targets.button": "Discover Targets",
174+
"storage.domains.connections.connection.host.label": "Select host: ",
175+
"storage.domains.connections.found.title": "Following connections were found:",
176+
"storage.domains.connections.connection.address.label": "Address: ",
177+
"storage.domains.connections.connection.port.label": "Port: ",
178+
"storage.domains.connections.connection.empty": "No connections found that aren't yet active",
173179
"storage.domains.connections.connection.detach.button": "Detach",
174180
"storage.domains.connections.domain.maintenance.warning": "Storage Domain is not in Maintenance mode",
175181
"storage.domains.connections.domain?.maintenance.warning.detail": "Connections cannot be attached or detached, cannot edit attached connections",

src/intl/messages.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,18 @@ const messageDescriptors = {
12261226
description: 'label for Storage Connections management dialog button',
12271227
},
12281228

1229+
storageConnectionsDiscoverTargets: {
1230+
id: 'storage.domains.connections.discover.targets.button',
1231+
defaultMessage: 'Discover Targets',
1232+
description: 'Button to discover targets based on search input',
1233+
},
1234+
1235+
storageConnectionsFoundTitle: {
1236+
id: 'storage.domains.connections.found.title',
1237+
defaultMessage: 'Following connections were found:',
1238+
description: 'Title above found targets from search input',
1239+
},
1240+
12291241
storageConnectionsDataError: {
12301242
id: 'storage.domains.connections.dataError',
12311243
defaultMessage: 'Could not fetch data needed for showing Storage Connections',
@@ -1292,6 +1304,30 @@ const messageDescriptors = {
12921304
description: 'add Connection button',
12931305
},
12941306

1307+
storageConnectionsHostSelectionLabel: {
1308+
id: 'storage.domains.connections.connection.host.label',
1309+
defaultMessage: 'Select host: ',
1310+
description: 'Label for selection of host',
1311+
},
1312+
1313+
storageConnectionAddressSelectionLabel: {
1314+
id: 'storage.domains.connections.connection.address.label',
1315+
defaultMessage: 'Address: ',
1316+
description: 'Label for selection of address',
1317+
},
1318+
1319+
storageConnectionPortSelectionLabel: {
1320+
id: 'storage.domains.connections.connection.port.label',
1321+
defaultMessage: 'Port: ',
1322+
description: 'Label for selection of port',
1323+
},
1324+
1325+
storageConnectionsEmptyList: {
1326+
id: 'storage.domains.connections.connection.empty',
1327+
defaultMessage: 'No connections found that aren\'t yet active',
1328+
description: 'Pop up message when empty list of discovered targets is returned',
1329+
},
1330+
12951331
storageConnectionsAttachConnectionButton: {
12961332
id: 'storage.domains.connections.connection.attach.button',
12971333
defaultMessage: 'Attach',

src/modals/storage-connections/StorageConnectionsDataProvider.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ const createConnection = async (connection) => {
1313
)
1414
}
1515

16+
const createAndAttachConnection = async (connection, domainId) => {
17+
const allConnections = await engineGet('api/storageconnections')
18+
const connectionsInList = allConnections.storage_connection.filter((con) => con.address === connection.address)
19+
if (connectionsInList.length === 1) {
20+
await attachConnection(connectionsInList[0].id, domainId)
21+
} else {
22+
const createdConnection = await createConnection(connection)
23+
await attachConnection(createdConnection.id, domainId)
24+
}
25+
}
26+
27+
const findConnections = async (ip, hostId, port) => {
28+
const connectionsFoundJson = await enginePost(
29+
`api/hosts/${hostId}/discoveriscsi`,
30+
JSON.stringify({ iscsi: { address: ip, port: port } })
31+
)
32+
return connectionsFoundJson.discovered_targets.iscsi_details
33+
}
34+
1635
const editConnection = async (connection, connectionId) => {
1736
return await enginePut(
1837
`api/storageconnections/${connectionId}`,
@@ -36,13 +55,17 @@ const detachConnection = async (connectionId, domainId) => {
3655
}
3756

3857
const fetchData = async (stgDomain) => {
58+
const headers = {
59+
'Cache-Control': 'no-cache',
60+
}
3961
if (!stgDomain?.id || !stgDomain?.dataCenterId) {
4062
throw new Error('StorageConnectionsDataProvider: invalid Storage Domain')
4163
}
42-
const [allConnectionsJson, storageDomain, domainConnectionsJson] = await Promise.all([
64+
const [allConnectionsJson, storageDomain, domainConnectionsJson, hostsJson] = await Promise.all([
4365
engineGet('api/storageconnections'),
4466
engineGet(`api/datacenters/${stgDomain.dataCenterId}/storagedomains/${stgDomain.id}`),
4567
engineGet(`api/storagedomains/${stgDomain.id}/storageconnections`),
68+
engineGet('api/hosts', headers),
4669
])
4770

4871
if (!storageDomain) {
@@ -51,6 +74,7 @@ const fetchData = async (stgDomain) => {
5174

5275
const allConnections = allConnectionsJson?.storage_connection
5376
const domainConnections = domainConnectionsJson?.storage_connection
77+
const hosts = hostsJson?.host.filter(host => host.status === 'up')
5478

5579
if (!allConnections || allConnections.error) {
5680
throw new Error('StorageConnectionsDataProvider: failed to fetch storage connections')
@@ -64,16 +88,17 @@ const fetchData = async (stgDomain) => {
6488
const domainConnectionsIds = new Set(domainConnections.map(connection => connection.id))
6589
const allConnectionsByTypeSorted = allConnections
6690
.filter(connection => connection.type === storageDomain.storage?.type)
67-
.map((conn) => {
68-
return { ...conn, isAttachedToDomain: domainConnectionsIds.has(conn.id) }
91+
.map((connection) => {
92+
return { ...connection, isAttachedToDomain: domainConnectionsIds.has(connection.id) }
6993
})
70-
.sort((conn1, conn2) => {
71-
return (conn1.isAttachedToDomain === conn2.isAttachedToDomain) ? 0 : conn1.isAttachedToDomain ? -1 : 1
94+
.sort((firstConnection, secondConnection) => {
95+
return (firstConnection.isAttachedToDomain === secondConnection.isAttachedToDomain) ? 0 : firstConnection.isAttachedToDomain ? -1 : 1
7296
})
7397

7498
return {
7599
storageDomain: storageDomain,
76100
connections: allConnectionsByTypeSorted,
101+
hosts: hosts,
77102
}
78103
}
79104

@@ -93,17 +118,20 @@ const StorageConnectionsDataProvider = ({ children, storageDomain }) => (
93118
return React.cloneElement(child, { isLoading: true })
94119
}
95120

96-
const { storageDomain, connections } = data
121+
const { storageDomain, connections, hosts } = data
97122

98123
return React.cloneElement(child, {
99124
storageDomain,
100125
connections,
126+
hosts,
101127
lastUpdated,
102128
doConnectionCreate: createConnection,
129+
doConnectionCreateAndAttach: createAndAttachConnection,
103130
doConnectionEdit: editConnection,
104131
doConnectionDelete: deleteConnection,
105132
doConnectionAttach: attachConnection,
106133
doConnectionDetach: detachConnection,
134+
doConnectionSearch: findConnections,
107135
doRefreshConnections: () => { fetchAndUpdateData() },
108136
})
109137
}}

src/modals/storage-connections/StorageConnectionsModal.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,27 @@ const StorageConnectionsModal = ({
1616
storageDomain,
1717
connections,
1818
onClose,
19+
hosts,
1920
doConnectionCreate = () => { },
2021
doConnectionEdit = () => { },
2122
doConnectionDelete = () => { },
2223
doConnectionAttach = () => { },
2324
doConnectionDetach = () => { },
2425
doRefreshConnections = () => { },
26+
doConnectionSearch = () => { },
27+
doConnectionCreateAndAttach = () => { },
2528
}) => {
2629
const [isOpen, setOpen] = useState(true)
2730
const [error, setError] = useState(null)
2831
const [isShowAll, setShowAll] = useState(false)
2932
const [shownConnections, setShownConnections] = useState(null)
33+
const [discoveredConnections, setDiscoveredConnections] = useState(null)
34+
const [shownHosts, setShownHosts] = useState(null)
3035

3136
useEffect(() => {
37+
setShownHosts(hosts)
3238
setShownConnections(isShowAll || !connections ? connections : connections.filter(conn => conn.isAttachedToDomain))
33-
}, [isShowAll, connections])
39+
}, [isShowAll, connections, hosts])
3440

3541
if (!connections) {
3642
return null
@@ -62,6 +68,27 @@ const StorageConnectionsModal = ({
6268
}
6369
}
6470

71+
const onConnectionSearch = async (ip, hostId, port) => {
72+
try {
73+
setDiscoveredConnections(null)
74+
const allConnections = await doConnectionSearch(ip, hostId, port)
75+
const filtered = allConnections.filter(item => {
76+
const match = connections.find(secondItem => secondItem.address === item.address)
77+
return !match || (match && !match.isAttachedToDomain)
78+
})
79+
setDiscoveredConnections(filtered)
80+
} catch (e) {
81+
setError(e.detail)
82+
}
83+
}
84+
85+
const onConnectionCreateAndAttach = async (connection, domainId) => {
86+
await doConnectionCreateAndAttach(connection, domainId)
87+
doRefreshConnections()
88+
const newdiscoveredList = discoveredConnections.filter(con => con.address !== connection.address)
89+
setDiscoveredConnections(newdiscoveredList)
90+
}
91+
6592
const onConnectionEdit = async (connection, connectionId) => {
6693
try {
6794
await doConnectionEdit(connection, connectionId)
@@ -119,7 +146,9 @@ const StorageConnectionsModal = ({
119146
<LoadingSpinner isLoading={isLoading}>
120147
<StorageConnectionsModalBody
121148
storageDomain={storageDomain}
149+
hosts={shownHosts}
122150
connections={shownConnections}
151+
foundConnections={discoveredConnections}
123152
isShowAll={isShowAll}
124153
setShowAll={setShowAll}
125154
error={error}
@@ -129,6 +158,8 @@ const StorageConnectionsModal = ({
129158
onDelete={onConnectionDelete}
130159
onAttach={onConnectionAttach}
131160
onDetach={onConnectionDetach}
161+
onSearch={onConnectionSearch}
162+
onCreateAndAttach={onConnectionCreateAndAttach}
132163
/>
133164
</LoadingSpinner>
134165
</PluginApiModal>
@@ -138,14 +169,17 @@ const StorageConnectionsModal = ({
138169
StorageConnectionsModal.propTypes = {
139170
isLoading: PropTypes.bool,
140171
storageDomain: PropTypes.object,
172+
hosts: PropTypes.array,
141173
connections: PropTypes.array,
142174
onClose: PropTypes.func.isRequired,
143175
doConnectionCreate: PropTypes.func,
144176
doConnectionEdit: PropTypes.func,
145177
doConnectionDelete: PropTypes.func,
146178
doConnectionAttach: PropTypes.func,
147179
doConnectionDetach: PropTypes.func,
180+
doConnectionSearch: PropTypes.func,
148181
doRefreshConnections: PropTypes.func,
182+
doConnectionCreateAndAttach: PropTypes.func,
149183
}
150184

151185
export default StorageConnectionsModal

0 commit comments

Comments
 (0)