diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 465a4006..697dbbcd 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -38,6 +38,13 @@ "dhcp_error": "We could not determine whether there is another DHCP server in the network.", "dhcp_static_ip_error": "In order to use DHCP server a static IP address must be set. We failed to determine if this network interface is configured using static IP address. Please set a static IP address manually.", "dhcp_dynamic_ip_found": "Your system uses dynamic IP address configuration for interface <0>{{interfaceName}}0>. In order to use DHCP server a static IP address must be set. Your current IP address is <0>{{ipAddress}}0>. We will automatically set this IP address as static if you press Enable DHCP button.", + "dhcp_lease_added": "Static lease \"{{key}}\" successfully added", + "dhcp_lease_deleted": "Static lease \"{{key}}\" successfully deleted", + "dhcp_new_static_lease": "New static lease", + "dhcp_static_leases_not_found": "No DHCP static leases found", + "dhcp_add_static_lease": "Add static lease", + "delete_confirm": "Are you sure you want to delete \"{{key}}\"?", + "form_enter_hostname": "Enter hostname", "error_details": "Error details", "back": "Back", "dashboard": "Dashboard", diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 39224388..a67aa987 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -662,41 +662,18 @@ 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 = values => async (dispatch, getState) => { const { config } = getState().dhcp; const updatedConfig = { ...config, ...values }; dispatch(setDhcpConfigRequest()); - if (values.interface_name) { - dispatch(findActiveDhcpRequest()); - try { - const activeDhcp = await apiClient.findActiveDhcp(values.interface_name); - dispatch(findActiveDhcpSuccess(activeDhcp)); - if (!activeDhcp.found) { - try { - await apiClient.setDhcpConfig(updatedConfig); - dispatch(setDhcpConfigSuccess(updatedConfig)); - dispatch(addSuccessToast('dhcp_config_saved')); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(setDhcpConfigFailure()); - } - } else { - dispatch(addErrorToast({ error: 'dhcp_found' })); - } - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(findActiveDhcpFailure()); - } - } else { - try { - await apiClient.setDhcpConfig(updatedConfig); - dispatch(setDhcpConfigSuccess(updatedConfig)); - dispatch(addSuccessToast('dhcp_config_saved')); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(setDhcpConfigFailure()); - } + dispatch(findActiveDhcp(values.interface_name)); + try { + await apiClient.setDhcpConfig(updatedConfig); + dispatch(setDhcpConfigSuccess(updatedConfig)); + dispatch(addSuccessToast('dhcp_config_saved')); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(setDhcpConfigFailure()); } }; @@ -704,40 +681,60 @@ 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) => { +export const toggleDhcp = values => async (dispatch) => { dispatch(toggleDhcpRequest()); + let config = { ...values, enabled: false }; + let successMessage = 'disabled_dhcp'; - if (config.enabled) { - try { - await apiClient.setDhcpConfig({ ...config, enabled: false }); - dispatch(toggleDhcpSuccess()); - dispatch(addSuccessToast('disabled_dhcp')); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(toggleDhcpFailure()); - } - } else { - dispatch(findActiveDhcpRequest()); - try { - const activeDhcp = await apiClient.findActiveDhcp(config.interface_name); - dispatch(findActiveDhcpSuccess(activeDhcp)); + if (!values.enabled) { + config = { ...values, enabled: true }; + successMessage = 'enabled_dhcp'; + dispatch(findActiveDhcp(values.interface_name)); + } - if (!activeDhcp.found) { - try { - await apiClient.setDhcpConfig({ ...config, enabled: true }); - dispatch(toggleDhcpSuccess()); - dispatch(addSuccessToast('enabled_dhcp')); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(toggleDhcpFailure()); - } - } else { - dispatch(addErrorToast({ error: 'dhcp_found' })); - } - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(findActiveDhcpFailure()); - } + try { + await apiClient.setDhcpConfig(config); + dispatch(toggleDhcpSuccess()); + dispatch(addSuccessToast(successMessage)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(toggleDhcpFailure()); + } +}; + +export const toggleLeaseModal = createAction('TOGGLE_LEASE_MODAL'); + +export const addStaticLeaseRequest = createAction('ADD_STATIC_LEASE_REQUEST'); +export const addStaticLeaseFailure = createAction('ADD_STATIC_LEASE_FAILURE'); +export const addStaticLeaseSuccess = createAction('ADD_STATIC_LEASE_SUCCESS'); + +export const addStaticLease = config => async (dispatch) => { + dispatch(addStaticLeaseRequest()); + try { + await apiClient.addStaticLease(config); + dispatch(addStaticLeaseSuccess()); + dispatch(addSuccessToast(t('dhcp_lease_added', { key: config.hostname }))); + dispatch(toggleLeaseModal()); + dispatch(getDhcpStatus()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(addStaticLeaseFailure()); + } +}; + +export const removeStaticLeaseRequest = createAction('REMOVE_STATIC_LEASE_REQUEST'); +export const removeStaticLeaseFailure = createAction('REMOVE_STATIC_LEASE_FAILURE'); +export const removeStaticLeaseSuccess = createAction('REMOVE_STATIC_LEASE_SUCCESS'); + +export const removeStaticLease = config => async (dispatch) => { + dispatch(removeStaticLeaseRequest()); + try { + await apiClient.removeStaticLease(config); + dispatch(removeStaticLeaseSuccess()); + dispatch(addSuccessToast(t('dhcp_lease_deleted', { key: config.hostname }))); + dispatch(getDhcpStatus()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(removeStaticLeaseFailure()); } }; diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 8f34f201..81bce7cf 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -318,6 +318,8 @@ export default class Api { DHCP_SET_CONFIG = { path: 'dhcp/set_config', method: 'POST' }; DHCP_FIND_ACTIVE = { path: 'dhcp/find_active_dhcp', method: 'POST' }; DHCP_INTERFACES = { path: 'dhcp/interfaces', method: 'GET' }; + DHCP_ADD_STATIC_LEASE = { path: 'dhcp/add_static_lease', method: 'POST' }; + DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' }; getDhcpStatus() { const { path, method } = this.DHCP_STATUS; @@ -347,6 +349,24 @@ export default class Api { return this.makeRequest(path, method, parameters); } + addStaticLease(config) { + const { path, method } = this.DHCP_ADD_STATIC_LEASE; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, + }; + return this.makeRequest(path, method, parameters); + } + + removeStaticLease(config) { + const { path, method } = this.DHCP_REMOVE_STATIC_LEASE; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, + }; + return this.makeRequest(path, method, parameters); + } + // Installation INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' }; INSTALL_CONFIGURE = { path: 'install/configure', method: 'POST' }; diff --git a/client/src/components/Settings/Dhcp/Form.js b/client/src/components/Settings/Dhcp/Form.js index af7c1931..86de6340 100644 --- a/client/src/components/Settings/Dhcp/Form.js +++ b/client/src/components/Settings/Dhcp/Form.js @@ -1,22 +1,97 @@ import React from 'react'; +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Field, reduxForm } from 'redux-form'; -import { withNamespaces } from 'react-i18next'; +import { Field, reduxForm, formValueSelector } from 'redux-form'; +import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; import { renderField, required, ipv4, isPositive, toNumber } from '../../../helpers/form'; -const Form = (props) => { +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 ( + + ); + }) +)); + +const renderInterfaceValues = (interfaceValues => ( +