Added check for active DHCP before enable

This commit is contained in:
Ildar Kamalov 2018-12-29 18:43:17 +03:00
parent d8802a9709
commit 568784b992
8 changed files with 282 additions and 150 deletions

View File

@ -1,5 +1,5 @@
{
"refresh_status": "Refresh status",
"check_dhcp_servers": "Check for DHCP servers",
"save_config": "Save config",
"enabled_dhcp": "DHCP server enabled",
"disabled_dhcp": "DHCP server disabled",

View File

@ -553,31 +553,14 @@ export const getDhcpInterfaces = () => async (dispatch) => {
}
};
export const setDhcpConfigRequest = createAction('SET_DHCP_CONFIG_REQUEST');
export const setDhcpConfigSuccess = createAction('SET_DHCP_CONFIG_SUCCESS');
export const setDhcpConfigFailure = createAction('SET_DHCP_CONFIG_FAILURE');
export const setDhcpConfig = config => async (dispatch) => {
dispatch(setDhcpConfigRequest());
try {
await apiClient.setDhcpConfig(config);
dispatch(getDhcpStatus());
dispatch(addSuccessToast('dhcp_config_saved'));
dispatch(setDhcpConfigSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(setDhcpConfigFailure());
}
};
export const findActiveDhcpRequest = createAction('FIND_ACTIVE_DHCP_REQUEST');
export const findActiveDhcpSuccess = createAction('FIND_ACTIVE_DHCP_SUCCESS');
export const findActiveDhcpFailure = createAction('FIND_ACTIVE_DHCP_FAILURE');
export const findActiveDhcp = () => async (dispatch) => {
export const findActiveDhcp = name => async (dispatch) => {
dispatch(findActiveDhcpRequest());
try {
const activeDhcp = await apiClient.findActiveDhcp();
const activeDhcp = await apiClient.findActiveDhcp(name);
dispatch(findActiveDhcpSuccess(activeDhcp));
} catch (error) {
dispatch(addErrorToast({ error }));
@ -585,27 +568,84 @@ export const findActiveDhcp = () => async (dispatch) => {
}
};
export const setDhcpConfigRequest = createAction('SET_DHCP_CONFIG_REQUEST');
export const setDhcpConfigSuccess = createAction('SET_DHCP_CONFIG_SUCCESS');
export const setDhcpConfigFailure = createAction('SET_DHCP_CONFIG_FAILURE');
// TODO rewrite findActiveDhcp part
export const setDhcpConfig = config => async (dispatch) => {
dispatch(setDhcpConfigRequest());
try {
if (config.interface_name) {
dispatch(findActiveDhcpRequest());
try {
const activeDhcp = await apiClient.findActiveDhcp(config.interface_name);
dispatch(findActiveDhcpSuccess(activeDhcp));
if (!activeDhcp.found) {
await apiClient.setDhcpConfig(config);
dispatch(addSuccessToast('dhcp_config_saved'));
dispatch(setDhcpConfigSuccess());
dispatch(getDhcpStatus());
} else {
dispatch(addErrorToast({ error: 'dhcp_found' }));
}
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(findActiveDhcpFailure());
}
} else {
await apiClient.setDhcpConfig(config);
dispatch(addSuccessToast('dhcp_config_saved'));
dispatch(setDhcpConfigSuccess());
dispatch(getDhcpStatus());
}
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(setDhcpConfigFailure());
}
};
export const toggleDhcpRequest = createAction('TOGGLE_DHCP_REQUEST');
export const toggleDhcpFailure = createAction('TOGGLE_DHCP_FAILURE');
export const toggleDhcpSuccess = createAction('TOGGLE_DHCP_SUCCESS');
// TODO rewrite findActiveDhcp part
export const toggleDhcp = config => async (dispatch) => {
dispatch(toggleDhcpRequest());
let successMessage = '';
try {
if (config.enabled) {
successMessage = 'disabled_dhcp';
if (config.enabled) {
dispatch(addSuccessToast('disabled_dhcp'));
try {
await apiClient.setDhcpConfig({ ...config, enabled: false });
} else {
successMessage = 'enabled_dhcp';
await apiClient.setDhcpConfig({ ...config, enabled: true });
dispatch(toggleDhcpSuccess());
dispatch(getDhcpStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleDhcpFailure());
}
} else {
dispatch(findActiveDhcpRequest());
try {
const activeDhcp = await apiClient.findActiveDhcp(config.interface_name);
dispatch(findActiveDhcpSuccess(activeDhcp));
dispatch(addSuccessToast(successMessage));
dispatch(toggleDhcpSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleDhcpFailure());
if (!activeDhcp.found) {
try {
await apiClient.setDhcpConfig({ ...config, enabled: true });
dispatch(toggleDhcpSuccess());
dispatch(getDhcpStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleDhcpFailure());
}
dispatch(addSuccessToast('enabled_dhcp'));
} else {
dispatch(addErrorToast({ error: 'dhcp_found' }));
}
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(findActiveDhcpFailure());
}
}
};

View File

@ -328,8 +328,12 @@ export default class Api {
return this.makeRequest(path, method, parameters);
}
findActiveDhcp() {
findActiveDhcp(name) {
const { path, method } = this.DHCP_FIND_ACTIVE;
return this.makeRequest(path, method);
const parameters = {
data: name,
headers: { 'Content-Type': 'text/plain' },
};
return this.makeRequest(path, method, parameters);
}
}

View File

@ -1,7 +1,6 @@
import React, { Fragment } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Field, reduxForm } from 'redux-form';
import { withNamespaces, Trans } from 'react-i18next';
import flow from 'lodash/flow';
@ -45,88 +44,17 @@ const renderField = ({
</Fragment>
);
const renderInterfaces = (interfaces => (
Object.keys(interfaces).map((item) => {
const option = interfaces[item];
const { name } = option;
const onlyIPv6 = option.ip_addresses.every(ip => ip.includes(':'));
let interfaceIP = option.ip_addresses[0];
if (!onlyIPv6) {
option.ip_addresses.forEach((ip) => {
if (!ip.includes(':')) {
interfaceIP = ip;
}
});
}
return (
<option value={name} key={name} disabled={onlyIPv6}>
{name} - {interfaceIP}
</option>
);
})
));
const renderInterfaceValues = (interfaceValues => (
<ul className="list-unstyled mt-1 mb-0">
<li>
<span className="interface__title">MTU: </span>
{interfaceValues.mtu}
</li>
<li>
<span className="interface__title"><Trans>dhcp_hardware_address</Trans>: </span>
{interfaceValues.hardware_address}
</li>
<li>
<span className="interface__title"><Trans>dhcp_ip_addresses</Trans>: </span>
{
interfaceValues.ip_addresses
.map(ip => <span key={ip} className="interface__ip">{ip}</span>)
}
</li>
</ul>
));
let Form = (props) => {
const Form = (props) => {
const {
t,
handleSubmit,
pristine,
submitting,
interfaces,
processing,
interfaceValue,
} = props;
return (
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12">
{!processing && interfaces &&
<div className="row">
<div className="col-sm-12 col-md-6">
<div className="form__group form__group--dhcp">
<label>{t('dhcp_interface_select')}</label>
<Field
name="interface_name"
component="select"
className="form-control custom-select"
>
<option value="">{t('dhcp_interface_select')}</option>
{renderInterfaces(interfaces)}
</Field>
</div>
</div>
{interfaceValue &&
<div className="col-sm-12 col-md-6">
{renderInterfaceValues(interfaces[interfaceValue])}
</div>
}
</div>
}
<hr/>
</div>
<div className="col-lg-6">
<div className="form__group form__group--dhcp">
<label>{t('dhcp_form_gateway_input')}</label>
@ -211,20 +139,10 @@ Form.propTypes = {
submitting: PropTypes.bool,
interfaces: PropTypes.object,
processing: PropTypes.bool,
interfaceValue: PropTypes.string,
initialValues: PropTypes.object,
t: PropTypes.func,
};
const selector = formValueSelector('dhcpForm');
Form = connect((state) => {
const interfaceValue = selector(state, 'interface_name');
return {
interfaceValue,
};
})(Form);
export default flow([
withNamespaces(),
reduxForm({ form: 'dhcpForm' }),

View File

@ -0,0 +1,113 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { withNamespaces, Trans } from 'react-i18next';
import flow from 'lodash/flow';
const renderInterfaces = (interfaces => (
Object.keys(interfaces).map((item) => {
const option = interfaces[item];
const { name } = option;
const onlyIPv6 = option.ip_addresses.every(ip => ip.includes(':'));
let interfaceIP = option.ip_addresses[0];
if (!onlyIPv6) {
option.ip_addresses.forEach((ip) => {
if (!ip.includes(':')) {
interfaceIP = ip;
}
});
}
return (
<option value={name} key={name} disabled={onlyIPv6}>
{name} - {interfaceIP}
</option>
);
})
));
const renderInterfaceValues = (interfaceValues => (
<ul className="list-unstyled mt-1 mb-0">
<li>
<span className="interface__title">MTU: </span>
{interfaceValues.mtu}
</li>
<li>
<span className="interface__title"><Trans>dhcp_hardware_address</Trans>: </span>
{interfaceValues.hardware_address}
</li>
<li>
<span className="interface__title"><Trans>dhcp_ip_addresses</Trans>: </span>
{
interfaceValues.ip_addresses
.map(ip => <span key={ip} className="interface__ip">{ip}</span>)
}
</li>
</ul>
));
let Interface = (props) => {
const {
t,
handleChange,
interfaces,
processing,
interfaceValue,
enabled,
} = props;
return (
<form>
{!processing && interfaces &&
<div className="row">
<div className="col-sm-12 col-md-6">
<div className="form__group form__group--dhcp">
<label>{t('dhcp_interface_select')}</label>
<Field
name="interface_name"
component="select"
className="form-control custom-select"
onChange={handleChange}
>
<option value="" disabled={enabled}>{t('dhcp_interface_select')}</option>
{renderInterfaces(interfaces)}
</Field>
</div>
</div>
{interfaceValue &&
<div className="col-sm-12 col-md-6">
{renderInterfaceValues(interfaces[interfaceValue])}
</div>
}
</div>
}
<hr/>
</form>
);
};
Interface.propTypes = {
handleChange: PropTypes.func,
interfaces: PropTypes.object,
processing: PropTypes.bool,
interfaceValue: PropTypes.string,
initialValues: PropTypes.object,
enabled: PropTypes.bool,
t: PropTypes.func,
};
const selector = formValueSelector('dhcpInterface');
Interface = connect((state) => {
const interfaceValue = selector(state, 'interface_name');
return {
interfaceValue,
};
})(Interface);
export default flow([
withNamespaces(),
reduxForm({ form: 'dhcpInterface' }),
])(Interface);

View File

@ -5,6 +5,7 @@ import { Trans, withNamespaces } from 'react-i18next';
import Form from './Form';
import Leases from './Leases';
import Interface from './Interface';
import Card from '../../ui/Card';
class Dhcp extends Component {
@ -12,27 +13,80 @@ class Dhcp extends Component {
this.props.setDhcpConfig(values);
};
handleRefresh = () => {
this.props.findActiveDhcp();
handleFormChange = (value) => {
this.props.setDhcpConfig(value);
}
handleToggle = (config) => {
this.props.toggleDhcp(config);
this.props.findActiveDhcp(config.interface_name);
}
getToggleDhcpButton = () => {
const { config } = this.props.dhcp;
const buttonText = config.enabled ? 'dhcp_disable' : 'dhcp_enable';
const buttonClass = config.enabled ? 'btn-gray' : 'btn-success';
const { config, active } = this.props.dhcp;
const activeDhcpFound = active && active.found;
const filledConfig = Object.keys(config).every((key) => {
if (key === 'enabled') {
return true;
}
return config[key];
});
if (config.enabled) {
return (
<button
type="button"
className="btn btn-standart mr-2 btn-gray"
onClick={() => this.props.toggleDhcp(config)}
>
<Trans>dhcp_disable</Trans>
</button>
);
}
return (
<button
type="button"
className={`btn btn-standart mr-2 ${buttonClass}`}
onClick={() => this.props.toggleDhcp(config)}
disabled={!config.interface_name}
className="btn btn-standart mr-2 btn-success"
onClick={() => this.handleToggle(config)}
disabled={!filledConfig || activeDhcpFound}
>
<Trans>{buttonText}</Trans>
<Trans>dhcp_enable</Trans>
</button>
);
}
getActiveDhcpMessage = () => {
const { active } = this.props.dhcp;
if (active) {
if (active.error) {
return (
<div className="text-danger">
{active.error}
</div>
);
}
return (
<Fragment>
{active.found ? (
<div className="text-danger">
<Trans>dhcp_found</Trans>
</div>
) : (
<div className="text-secondary">
<Trans>dhcp_not_found</Trans>
</div>
)}
</Fragment>
);
}
return '';
}
render() {
const { t, dhcp } = this.props;
const statusButtonClass = classnames({
@ -42,14 +96,20 @@ class Dhcp extends Component {
return (
<Fragment>
{!dhcp.processing &&
<Card title={ t('dhcp_title') } subtitle={ t('dhcp_description') } bodyType="card-body box-body--settings">
<div className="row">
<div className="col">
<Card title={ t('dhcp_title') } subtitle={ t('dhcp_description') } bodyType="card-body box-body--settings">
<div className="dhcp">
{!dhcp.processing &&
<Fragment>
<Interface
onChange={this.handleFormChange}
initialValues={dhcp.config}
interfaces={dhcp.interfaces}
processing={dhcp.processingInterfaces}
enabled={dhcp.config.enabled}
/>
<Form
onSubmit={this.handleFormSubmit}
initialValues={dhcp.config}
enabled={dhcp.config.enabled}
interfaces={dhcp.interfaces}
processing={dhcp.processingInterfaces}
/>
@ -57,29 +117,21 @@ class Dhcp extends Component {
<div className="card-actions mb-3">
{this.getToggleDhcpButton()}
<button
className={statusButtonClass}
type="button"
onClick={this.handleRefresh}
className={statusButtonClass}
onClick={() =>
this.props.findActiveDhcp(dhcp.config.interface_name)
}
disabled={!dhcp.config.interface_name}
>
<Trans>refresh_status</Trans>
<Trans>check_dhcp_servers</Trans>
</button>
</div>
{dhcp.active &&
<div className="text-secondary">
{dhcp.active.found ? (
<span className="text-danger">
<Trans>dhcp_found</Trans>
</span>
) : (
<Trans>dhcp_not_found</Trans>
)}
</div>
}
</div>
</div>
</Card>
}
{this.getActiveDhcpMessage()}
</Fragment>
}
</div>
</Card>
{!dhcp.processing && dhcp.config.enabled &&
<Card title={ t('dhcp_leases') } bodyType="card-body box-body--settings">
<div className="row">

View File

@ -44,3 +44,7 @@
.interface__ip:last-child:after {
content: "";
}
.dhcp {
min-height: 450px;
}

View File

@ -304,6 +304,7 @@ const dhcp = handleActions({
config: {
enabled: false,
},
active: null,
leases: [],
});