import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import AceEditor from 'react-ace';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import { sortBy } from 'lodash';
import AddHostModal from 'components/hosts/AddHostModal';
import Button from 'components/buttons/Button';
import configInterface from 'interfaces/config';
import HostContainer from 'components/hosts/HostContainer';
import HostPagination from 'components/hosts/HostPagination';
import HostSidePanel from 'components/side_panels/HostSidePanel';
import LabelForm from 'components/forms/LabelForm';
import Modal from 'components/modals/Modal';
import QuerySidePanel from 'components/side_panels/QuerySidePanel';
import labelInterface from 'interfaces/label';
import hostInterface from 'interfaces/host';
import osqueryTableInterface from 'interfaces/osquery_table';
import statusLabelsInterface from 'interfaces/status_labels';
import enrollSecretInterface from 'interfaces/enroll_secret';
import { selectOsqueryTable } from 'redux/nodes/components/QueryPages/actions';
import {
getStatusLabelCounts,
setPagination,
} from 'redux/nodes/components/ManageHostsPage/actions';
import hostActions from 'redux/nodes/entities/hosts/actions';
import labelActions from 'redux/nodes/entities/labels/actions';
import { renderFlash } from 'redux/nodes/notifications/actions';
import entityGetter from 'redux/utilities/entityGetter';
import PATHS from 'router/paths';
import deepDifference from 'utilities/deep_difference';
import scrollToTop from 'utilities/scroll_to_top';
import helpers from './helpers';
const NEW_LABEL_HASH = '#new_label';
const baseClass = 'manage-hosts';
export class ManageHostsPage extends PureComponent {
static propTypes = {
config: configInterface,
dispatch: PropTypes.func,
hosts: PropTypes.arrayOf(hostInterface),
isAddLabel: PropTypes.bool,
labelErrors: PropTypes.shape({
base: PropTypes.string,
}),
labels: PropTypes.arrayOf(labelInterface),
loadingHosts: PropTypes.bool.isRequired,
loadingLabels: PropTypes.bool.isRequired,
enrollSecret: enrollSecretInterface,
selectedFilter: PropTypes.string,
selectedLabel: labelInterface,
selectedOsqueryTable: osqueryTableInterface,
statusLabels: statusLabelsInterface,
page: PropTypes.number,
perPage: PropTypes.number,
};
static defaultProps = {
page: 1,
perPage: 100,
loadingHosts: false,
loadingLabels: false,
};
constructor (props) {
super(props);
this.state = {
isEditLabel: false,
labelQueryText: '',
pagedHosts: [],
showDeleteHostModal: false,
showAddHostModal: false,
selectedHost: null,
showDeleteLabelModal: false,
showHostContainerSpinner: false,
};
}
componentDidMount () {
const { dispatch, page, perPage, selectedFilter } = this.props;
dispatch(setPagination(page, perPage, selectedFilter));
}
componentWillUnmount () {
this.clearHostUpdates();
return false;
}
onAddLabelClick = (evt) => {
evt.preventDefault();
const { dispatch } = this.props;
dispatch(push(`${PATHS.MANAGE_HOSTS}${NEW_LABEL_HASH}`));
return false;
}
onCancelAddLabel = () => {
const { dispatch } = this.props;
dispatch(push(PATHS.MANAGE_HOSTS));
return false;
}
onAddHostClick = (evt) => {
evt.preventDefault();
const { toggleAddHostModal } = this;
toggleAddHostModal();
return false;
}
onDestroyHost = (evt) => {
evt.preventDefault();
const { dispatch } = this.props;
const { selectedHost } = this.state;
dispatch(hostActions.destroy(selectedHost))
.then(() => {
this.toggleDeleteHostModal(null)();
dispatch(getStatusLabelCounts);
dispatch(renderFlash('success', `Host "${selectedHost.hostname}" was successfully deleted`));
});
return false;
}
onEditLabel = (formData) => {
const { dispatch, selectedLabel } = this.props;
const updateAttrs = deepDifference(formData, selectedLabel);
return dispatch(labelActions.update(selectedLabel, updateAttrs))
.then(() => {
this.toggleEditLabel();
return false;
})
.catch(() => false);
}
onLabelClick = (selectedLabel) => {
return (evt) => {
evt.preventDefault();
const { dispatch, perPage } = this.props;
const { MANAGE_HOSTS } = PATHS;
const { slug, type } = selectedLabel;
const nextLocation = type === 'all' ? MANAGE_HOSTS : `${MANAGE_HOSTS}/${slug}`;
dispatch(push(nextLocation));
dispatch(setPagination(1, perPage, selectedLabel.slug));
return false;
};
}
onOsqueryTableSelect = (tableName) => {
const { dispatch } = this.props;
dispatch(selectOsqueryTable(tableName));
return false;
}
onPaginationChange = (page) => {
const { dispatch, selectedFilter, perPage } = this.props;
dispatch(setPagination(page, perPage, selectedFilter));
scrollToTop();
return true;
}
onSaveAddLabel = (formData) => {
const { dispatch } = this.props;
return dispatch(labelActions.create(formData))
.then(() => {
dispatch(push(PATHS.MANAGE_HOSTS));
return false;
});
}
onDeleteLabel = () => {
const { toggleDeleteLabelModal } = this;
const { dispatch, selectedLabel } = this.props;
const { MANAGE_HOSTS } = PATHS;
return dispatch(labelActions.destroy(selectedLabel))
.then(() => {
toggleDeleteLabelModal();
dispatch(push(MANAGE_HOSTS));
return false;
});
}
onQueryHost = (host) => {
return (evt) => {
evt.preventDefault();
const { dispatch } = this.props;
const { NEW_QUERY } = PATHS;
dispatch(push({
pathname: NEW_QUERY,
query: { host_ids: [host.id] },
}));
return false;
};
}
clearHostUpdates () {
if (this.timeout) {
global.window.clearTimeout(this.timeout);
this.timeout = null;
}
}
filterAllHosts = (hosts, selectedLabel) => {
const { filterHosts } = helpers;
return filterHosts(hosts, selectedLabel);
}
sortHosts = (hosts) => {
return sortBy(hosts, (h) => { return h.hostname; });
}
toggleAddHostModal = () => {
const { showAddHostModal } = this.state;
this.setState({ showAddHostModal: !showAddHostModal });
return false;
}
toggleDeleteHostModal = (selectedHost) => {
return () => {
const { showDeleteHostModal } = this.state;
this.setState({
selectedHost,
showDeleteHostModal: !showDeleteHostModal,
});
return false;
};
}
toggleDeleteLabelModal = () => {
const { showDeleteLabelModal } = this.state;
this.setState({ showDeleteLabelModal: !showDeleteLabelModal });
return false;
}
toggleEditLabel = () => {
const { isEditLabel } = this.state;
this.setState({ isEditLabel: !isEditLabel });
return false;
}
renderAddHostModal = () => {
const { toggleAddHostModal } = this;
const { showAddHostModal } = this.state;
const { enrollSecret, config } = this.props;
if (!showAddHostModal) {
return false;
}
return (
This action will delete the host {selectedHost.hostname} from your Fleet instance. If the host comes back online it will automatically re-enroll. To prevent the host from re-enrolling please disable or uninstall osquery on the host. Are you sure you wish to delete this label?
{description || {defaultDescription}}
{hostsTotalDisplay}