mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
UI Updates for receptor peering
This commit is contained in:
committed by
Seth Foster
parent
6cb2cd18b0
commit
46dc61253f
@@ -29,6 +29,7 @@ import Notifications from './models/Notifications';
|
|||||||
import Organizations from './models/Organizations';
|
import Organizations from './models/Organizations';
|
||||||
import ProjectUpdates from './models/ProjectUpdates';
|
import ProjectUpdates from './models/ProjectUpdates';
|
||||||
import Projects from './models/Projects';
|
import Projects from './models/Projects';
|
||||||
|
import ReceptorAddresses from './models/Receptor';
|
||||||
import Roles from './models/Roles';
|
import Roles from './models/Roles';
|
||||||
import Root from './models/Root';
|
import Root from './models/Root';
|
||||||
import Schedules from './models/Schedules';
|
import Schedules from './models/Schedules';
|
||||||
@@ -79,6 +80,7 @@ const NotificationsAPI = new Notifications();
|
|||||||
const OrganizationsAPI = new Organizations();
|
const OrganizationsAPI = new Organizations();
|
||||||
const ProjectUpdatesAPI = new ProjectUpdates();
|
const ProjectUpdatesAPI = new ProjectUpdates();
|
||||||
const ProjectsAPI = new Projects();
|
const ProjectsAPI = new Projects();
|
||||||
|
const ReceptorAPI = new ReceptorAddresses()
|
||||||
const RolesAPI = new Roles();
|
const RolesAPI = new Roles();
|
||||||
const RootAPI = new Root();
|
const RootAPI = new Root();
|
||||||
const SchedulesAPI = new Schedules();
|
const SchedulesAPI = new Schedules();
|
||||||
@@ -130,6 +132,7 @@ export {
|
|||||||
OrganizationsAPI,
|
OrganizationsAPI,
|
||||||
ProjectUpdatesAPI,
|
ProjectUpdatesAPI,
|
||||||
ProjectsAPI,
|
ProjectsAPI,
|
||||||
|
ReceptorAPI,
|
||||||
RolesAPI,
|
RolesAPI,
|
||||||
RootAPI,
|
RootAPI,
|
||||||
SchedulesAPI,
|
SchedulesAPI,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Instances extends Base {
|
|||||||
this.readHealthCheckDetail = this.readHealthCheckDetail.bind(this);
|
this.readHealthCheckDetail = this.readHealthCheckDetail.bind(this);
|
||||||
this.healthCheck = this.healthCheck.bind(this);
|
this.healthCheck = this.healthCheck.bind(this);
|
||||||
this.readInstanceGroup = this.readInstanceGroup.bind(this);
|
this.readInstanceGroup = this.readInstanceGroup.bind(this);
|
||||||
|
this.readReceptorAddresses = this.readReceptorAddresses.bind(this);
|
||||||
this.deprovisionInstance = this.deprovisionInstance.bind(this);
|
this.deprovisionInstance = this.deprovisionInstance.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +28,10 @@ class Instances extends Base {
|
|||||||
return this.http.get(`${this.baseUrl}${instanceId}/instance_groups/`);
|
return this.http.get(`${this.baseUrl}${instanceId}/instance_groups/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readReceptorAddresses(instanceId) {
|
||||||
|
return this.http.get(`${this.baseUrl}${instanceId}/receptor_addresses/`);
|
||||||
|
}
|
||||||
|
|
||||||
deprovisionInstance(instanceId) {
|
deprovisionInstance(instanceId) {
|
||||||
return this.http.patch(`${this.baseUrl}${instanceId}/`, {
|
return this.http.patch(`${this.baseUrl}${instanceId}/`, {
|
||||||
node_state: 'deprovisioning',
|
node_state: 'deprovisioning',
|
||||||
|
|||||||
10
awx/ui/src/api/models/Receptor.js
Normal file
10
awx/ui/src/api/models/Receptor.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class ReceptorAddresses extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = 'api/v2/receptor_addresses/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReceptorAddresses;
|
||||||
@@ -16,7 +16,7 @@ import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
|
|||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
||||||
import DataListToolbar from 'components/DataListToolbar';
|
import DataListToolbar from 'components/DataListToolbar';
|
||||||
import { InstancesAPI } from 'api';
|
import { InstancesAPI, ReceptorAPI } from 'api';
|
||||||
import useExpanded from 'hooks/useExpanded';
|
import useExpanded from 'hooks/useExpanded';
|
||||||
import useSelected from 'hooks/useSelected';
|
import useSelected from 'hooks/useSelected';
|
||||||
import InstancePeerListItem from './InstancePeerListItem';
|
import InstancePeerListItem from './InstancePeerListItem';
|
||||||
@@ -24,7 +24,7 @@ import InstancePeerListItem from './InstancePeerListItem';
|
|||||||
const QS_CONFIG = getQSConfig('peer', {
|
const QS_CONFIG = getQSConfig('peer', {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 20,
|
page_size: 20,
|
||||||
order_by: 'hostname',
|
order_by: 'pk',
|
||||||
});
|
});
|
||||||
|
|
||||||
function InstancePeerList({ setBreadcrumb }) {
|
function InstancePeerList({ setBreadcrumb }) {
|
||||||
@@ -50,14 +50,28 @@ function InstancePeerList({ setBreadcrumb }) {
|
|||||||
data: { results, count: itemNumber },
|
data: { results, count: itemNumber },
|
||||||
},
|
},
|
||||||
actions,
|
actions,
|
||||||
|
instances,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
InstancesAPI.readDetail(id),
|
InstancesAPI.readDetail(id),
|
||||||
InstancesAPI.readPeers(id, params),
|
InstancesAPI.readPeers(id, params),
|
||||||
InstancesAPI.readOptions(),
|
InstancesAPI.readOptions(),
|
||||||
|
InstancesAPI.read(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const address_list = []
|
||||||
|
|
||||||
|
for(let q = 0; q < results.length; q++) {
|
||||||
|
const receptor = results[q];
|
||||||
|
const host = instances.data.results.filter((obj) => obj.id === receptor.instance)[0];
|
||||||
|
const copy = receptor;
|
||||||
|
copy.hostname = host.hostname;
|
||||||
|
copy.node_type = host.node_type;
|
||||||
|
address_list.push(copy);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instance: detail,
|
instance: detail,
|
||||||
peers: results,
|
peers: address_list,
|
||||||
count: itemNumber,
|
count: itemNumber,
|
||||||
relatedSearchableKeys: (actions?.data?.related_search_fields || []).map(
|
relatedSearchableKeys: (actions?.data?.related_search_fields || []).map(
|
||||||
(val) => val.slice(0, -8)
|
(val) => val.slice(0, -8)
|
||||||
@@ -90,15 +104,60 @@ function InstancePeerList({ setBreadcrumb }) {
|
|||||||
useSelected(peers);
|
useSelected(peers);
|
||||||
|
|
||||||
const fetchInstancesToAssociate = useCallback(
|
const fetchInstancesToAssociate = useCallback(
|
||||||
(params) =>
|
async (params) => {
|
||||||
InstancesAPI.read(
|
const address_list = []
|
||||||
|
|
||||||
|
const instances = await InstancesAPI.read(
|
||||||
mergeParams(params, {
|
mergeParams(params, {
|
||||||
...{ not__id: id },
|
|
||||||
...{ not__node_type: ['control', 'hybrid'] },
|
...{ not__node_type: ['control', 'hybrid'] },
|
||||||
...{ not__hostname: instance.peers },
|
}))
|
||||||
})
|
const receptors = (await ReceptorAPI.read()).data.results;
|
||||||
),
|
|
||||||
[id, instance]
|
// get instance ids of the current peered receptor ids
|
||||||
|
const already_peered_instance_ids = []
|
||||||
|
for(let h =0; h < instance.peers.length; h++) {
|
||||||
|
const matched = receptors.filter((obj) => obj.id === instance.peers[h]);
|
||||||
|
matched.forEach(element => {
|
||||||
|
already_peered_instance_ids.push(element.instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let q = 0; q < receptors.length; q++) {
|
||||||
|
const receptor = receptors[q];
|
||||||
|
|
||||||
|
if(already_peered_instance_ids.includes(receptor.instance)) {
|
||||||
|
// ignore reverse peers
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instance.peers.includes(receptor.id)) {
|
||||||
|
// no links to existing links
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instance.id === receptor.instance) {
|
||||||
|
// no links to thy self
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = instances.data.results.filter((obj) => obj.id === receptor.instance)[0];
|
||||||
|
|
||||||
|
if(host === undefined) {
|
||||||
|
// no hosts
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const copy = receptor;
|
||||||
|
copy.hostname = host.hostname;
|
||||||
|
copy.node_type = host.node_type;
|
||||||
|
address_list.push(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
instances.data.results = address_list;
|
||||||
|
|
||||||
|
return instances;
|
||||||
|
},
|
||||||
|
[instance]
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -108,17 +167,20 @@ function InstancePeerList({ setBreadcrumb }) {
|
|||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(
|
useCallback(
|
||||||
async (instancesPeerToAssociate) => {
|
async (instancesPeerToAssociate) => {
|
||||||
const selected_hostname = instancesPeerToAssociate.map(
|
|
||||||
(obj) => obj.hostname
|
const selected_peers = instancesPeerToAssociate.map(
|
||||||
|
(obj) => obj.id
|
||||||
);
|
);
|
||||||
|
|
||||||
const new_peers = [
|
const new_peers = [
|
||||||
...new Set([...instance.peers, ...selected_hostname]),
|
...new Set([...instance.peers, ...selected_peers]),
|
||||||
];
|
];
|
||||||
await InstancesAPI.update(instance.id, { peers: new_peers });
|
await InstancesAPI.update(instance.id, { peers: new_peers });
|
||||||
|
|
||||||
fetchPeers();
|
fetchPeers();
|
||||||
addToast({
|
addToast({
|
||||||
id: instancesPeerToAssociate,
|
id: instancesPeerToAssociate,
|
||||||
title: t`${selected_hostname} added as a peer. Please be sure to run the install bundle for ${instance.hostname} again in order to see changes take effect.`,
|
title: t`Peers update on ${instance.hostname}. Please be sure to run the install bundle for ${instance.hostname} again in order to see changes take effect.`,
|
||||||
variant: AlertVariant.success,
|
variant: AlertVariant.success,
|
||||||
hasTimeout: true,
|
hasTimeout: true,
|
||||||
});
|
});
|
||||||
@@ -133,17 +195,18 @@ function InstancePeerList({ setBreadcrumb }) {
|
|||||||
error: disassociateError,
|
error: disassociateError,
|
||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
const new_peers = [];
|
let new_peers = instance.peers;
|
||||||
const selected_hostname = selected.map((obj) => obj.hostname);
|
|
||||||
for (let i = 0; i < instance.peers.length; i++) {
|
const selected_ids = selected.map((obj) => obj.id);
|
||||||
if (!selected_hostname.includes(instance.peers[i])) {
|
|
||||||
new_peers.push(instance.peers[i]);
|
for (let i = 0; i < selected_ids.length; i++) {
|
||||||
}
|
new_peers = new_peers.filter((s_id) => s_id !== selected_ids[i]);
|
||||||
}
|
}
|
||||||
await InstancesAPI.update(instance.id, { peers: new_peers });
|
await InstancesAPI.update(instance.id, { peers: new_peers });
|
||||||
|
|
||||||
fetchPeers();
|
fetchPeers();
|
||||||
addToast({
|
addToast({
|
||||||
title: t`${selected_hostname} removed. Please be sure to run the install bundle for ${instance.hostname} again in order to see changes take effect.`,
|
title: t`Peer removed. Please be sure to run the install bundle for ${instance.hostname} again in order to see changes take effect.`,
|
||||||
variant: AlertVariant.success,
|
variant: AlertVariant.success,
|
||||||
hasTimeout: true,
|
hasTimeout: true,
|
||||||
});
|
});
|
||||||
@@ -187,6 +250,8 @@ function InstancePeerList({ setBreadcrumb }) {
|
|||||||
]}
|
]}
|
||||||
headerRow={
|
headerRow={
|
||||||
<HeaderRow qsConfig={QS_CONFIG} isExpandable>
|
<HeaderRow qsConfig={QS_CONFIG} isExpandable>
|
||||||
|
<HeaderCell sortKey="address">{t`Address`}</HeaderCell>
|
||||||
|
<HeaderCell sortKey="port">{t`Port`}</HeaderCell>
|
||||||
<HeaderCell
|
<HeaderCell
|
||||||
tooltip={t`Cannot run health check on hop nodes.`}
|
tooltip={t`Cannot run health check on hop nodes.`}
|
||||||
sortKey="hostname"
|
sortKey="hostname"
|
||||||
@@ -243,10 +308,12 @@ function InstancePeerList({ setBreadcrumb }) {
|
|||||||
isModalOpen={isModalOpen}
|
isModalOpen={isModalOpen}
|
||||||
onAssociate={handlePeerAssociate}
|
onAssociate={handlePeerAssociate}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
title={t`Select Instances`}
|
title={t`Select Peer Addresses`}
|
||||||
optionsRequest={readInstancesOptions}
|
optionsRequest={readInstancesOptions}
|
||||||
displayKey="hostname"
|
displayKey="hostname"
|
||||||
columns={[
|
columns={[
|
||||||
|
{ key: 'address', name: t`Address` },
|
||||||
|
{ key: 'port', name: t`Port` },
|
||||||
{ key: 'hostname', name: t`Name` },
|
{ key: 'hostname', name: t`Name` },
|
||||||
{ key: 'node_type', name: t`Node Type` },
|
{ key: 'node_type', name: t`Node Type` },
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -43,8 +43,21 @@ function InstancePeerListItem({
|
|||||||
}}
|
}}
|
||||||
dataLabel={t`Selected`}
|
dataLabel={t`Selected`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Td id={labelId} dataLabel={t`Address`}>
|
||||||
|
<Link to={`/instances/${peerInstance.instance}/details`}>
|
||||||
|
<b>{peerInstance.address}</b>
|
||||||
|
</Link>
|
||||||
|
</Td>
|
||||||
|
|
||||||
|
<Td id={labelId} dataLabel={t`Port`}>
|
||||||
|
<Link to={`/instances/${peerInstance.instance}/details`}>
|
||||||
|
<b>{peerInstance.port}</b>
|
||||||
|
</Link>
|
||||||
|
</Td>
|
||||||
|
|
||||||
<Td id={labelId} dataLabel={t`Name`}>
|
<Td id={labelId} dataLabel={t`Name`}>
|
||||||
<Link to={`/instances/${peerInstance.id}/details`}>
|
<Link to={`/instances/${peerInstance.instance}/details`}>
|
||||||
<b>{peerInstance.hostname}</b>
|
<b>{peerInstance.hostname}</b>
|
||||||
</Link>
|
</Link>
|
||||||
</Td>
|
</Td>
|
||||||
|
|||||||
Reference in New Issue
Block a user