Loading js-apps/frontend/src/js/app-constants.js +5 −1 Original line number Diff line number Diff line Loading @@ -2,6 +2,10 @@ * Copyright (c) 2020 ETSI. All rights reserved. */ // Version export const MEC_SANDBOX_VERSION = 'v1.6'; // Host export const HOST_PATH = location.origin; // Pages Loading Loading @@ -57,7 +61,7 @@ export const DIALOG_SESSION_TERMINATED = 'DIALOG_SESSION_TERMINATED'; export const DIALOG_HELP_GETTING_STARTED = 'DIALOG_HELP_GETTING_STARTED'; export const DIALOG_CONFIRM_DELETE_APP = 'DIALOG_CONFIRM_DELETE_APP'; export const DIALOG_CREATE_NEW_APP = 'DIALOG_CREATE_NEW_APP'; export const DIALOG_NEWS = 'DIALOG_NEWS'; export const DIALOG_VERSION = 'DIALOG_VERSION'; // Alert Types export const ALERT_DEGRADED_NETWORK = 'ALERT_DEGRADED_NETWORK'; Loading js-apps/frontend/src/js/components/dialogs/news-dialog.jsdeleted 100644 → 0 +0 −75 Original line number Diff line number Diff line /* * Copyright (c) 2020 ETSI. All rights reserved. */ import React, { Component } from 'react'; import BasicDialog from './basic-dialog'; import { Typography } from '@rmwc/typography'; import { WIKI_HELP_URL, WIKI_DISCUSSION_BOARD_URL } from '../../app-constants'; class NewsDialog extends Component { constructor(props) { super(props); } render() { return ( <BasicDialog title={this.props.title} open={this.props.open} onSubmit={this.props.onClose} onClose={this.props.onClose} submitLabel = {'Ok'} > <div style={styles.text}> <Typography theme="primary" use="body1"> <p><b>November 2021</b></p> </Typography> <Typography use="body1"> WebSocket support for subscriptions per MEC009: <ul> <li>MEC028 - WLAN Access Information API (v2.2.1)</li> </ul> </Typography> <Typography theme="primary" use="body1"> <p><b>October 2021</b></p> </Typography> <Typography use="body1"> The following APIs are supported as Beta features: <ul> <li>MEC011 - Edge Platform Application Enablement (v2.1.1)</li> <li>MEC021 - Application Mobility Service API (v2.1.1)</li> </ul> <p> Usage details are available in the <a href={WIKI_HELP_URL} target="_blank">MEC Sandbox Wiki</a><br/> Questions & feedback are appreciated on the <a href={WIKI_DISCUSSION_BOARD_URL} target="_blank">Slack Discussion Board</a> </p> <p> Thank you in advance for your collaboration in trying out these new features. </p> <p style={styles.signature}> The MEC Sandbox Team </p> </Typography> </div> </BasicDialog> ); } } const styles = { text: { // color: 'black', marginLeft: 10, marginRight: 15 }, signature: { textAlign: 'right' } }; export default NewsDialog; js-apps/frontend/src/js/components/dialogs/version-dialog.js 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (c) 2020 ETSI. All rights reserved. */ import React, { Component } from 'react'; import BasicDialog from './basic-dialog'; import { Typography } from '@rmwc/typography'; class VersionDialog extends Component { constructor(props) { super(props); } render() { return ( <BasicDialog title={this.props.title} open={this.props.open} onSubmit={this.props.onClose} onClose={this.props.onClose} submitLabel = {'Ok'} > <div style={styles.text}> <Typography theme="primary" use="subtitle1"><p><b>v1.6 •</b> 2021-12-16</p></Typography> <Typography use="body1"> Final STF599 update: <ul> <li>AdvantEDGE baseline upgrade to v1.8.1</li> <li>General maintenance fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5.3 (beta) •</b> 2021-11-22</p></Typography> <Typography use="body1"> Maintenance Release: <ul> <li>Service IDs displayed in frontend</li> <li>Garbage collector for internal databases</li> <li>User-persistent sandbox names</li> <li>MEC Service bug fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5.2 (beta) •</b> 2021-11-08</p></Typography> <Typography use="body1"> MEC028 WebSockets: <ul> <li>MEC028 subscriptions - support for MEC009 WebSocket fallback pattern</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5.1 (beta) •</b> 2021-10-30</p></Typography> <Typography use="body1"> MEC API Maintenance: <ul> <li>MEC Service bug fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5 (beta) •</b> 2021-09-30</p></Typography> <Typography use="body1"> New MEC APIs: <ul> <li>MEC011 - Edge Platform Application Enablement</li> <li>MEC021 - Application Mobility Service</li> <li>Dual MEP network to showcase new APIs</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.4.1 •</b> 2021-08-06</p></Typography> <Typography use="body1"> MEC Service Maintenance: <ul> <li>MEC Service bug fixes</li> <li>Beta tags removed from API endpoints</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.4 (beta) •</b> 2021-07-20</p></Typography> <Typography use="body1"> MEC Service Enhancements: <ul> <li>MEC012, MEC013 & MEC028 support for all API endpoints</li> <li>MEC Service bug fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.3 •</b> 2021-06-03</p></Typography> <Typography use="body1"> Long-term Sandbox Analytics Storage: <ul> <li>Metrics object storage deployment</li> <li>Thanos integration for metrics data long-term storage</li> <li>Maintenance fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.2 •</b> 2021-05-11</p></Typography> <Typography use="body1"> MEC Sandbox VM Upgrade: <ul> <li>CPU/Memory upgrade</li> <li>Maintenance fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.1 •</b> 2021-03-23</p></Typography> <Typography use="body1"> Monitoring & Analytics: <ul> <li>Session statistics</li> <li>MEC Service & Platform API metrics</li> <li>Alerting framework</li> <li>Maintenance fixes</li> </ul> </Typography> </div> </BasicDialog> ); } } const styles = { text: { // color: 'black', marginLeft: 10, marginRight: 15 }, signature: { textAlign: 'right' } }; export default VersionDialog; js-apps/frontend/src/js/containers/app-container.js +105 −11 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ import SessionTerminatedDialog from '../components/dialogs/session-terminated-di import HelpGettingStartedDialog from '../components/dialogs/help-getting-started-dialog'; import ConfirmDeleteAppDialog from '../components/dialogs/confirm-delete-app-dialog'; import CreateNewAppDialog from '../components/dialogs/create-new-app-dialog'; import NewsDialog from '../components/dialogs/news-dialog'; import VersionDialog from '../components/dialogs/version-dialog'; import * as meepAuthSvcRestApiClient from '../../../../../js-packages/meep-auth-svc-client/src/index.js'; import * as meepPlatformCtrlRestApiClient from '../../../../../js-packages/meep-platform-ctrl-client/src/index.js'; Loading @@ -45,7 +45,7 @@ import { DIALOG_HELP_GETTING_STARTED, DIALOG_CONFIRM_DELETE_APP, DIALOG_CREATE_NEW_APP, DIALOG_NEWS, DIALOG_VERSION, ALERT_DEGRADED_NETWORK, STATUS_SIGNED_IN, STATUS_SIGNING_IN, Loading Loading @@ -86,6 +86,7 @@ import { uiSandboxChangeUpdateUeInProgressCount, uiSandboxChangeUpdateAutomationInProgressCount, uiSandboxChangeUpdateAppInstancesInProgressCount, uiSandboxChangeTerminationInProgressCount, uiSandboxChangeActivationInProgressCount, uiSandboxChangeActivationInProgressScenarioName } from '../state/ui'; Loading Loading @@ -169,6 +170,9 @@ class AppContainer extends Component { this.meepMonEngineApi = new meepMonEngineRestApiClient.PodStatesApi(); // Initialize undefined states if (this.props.terminationInProgressCount === undefined) { this.props.changeTerminationInProgressCount(-1); } if (this.props.activationInProgressCount === undefined) { this.props.changeActivationInProgressCount(-1); } Loading Loading @@ -301,6 +305,7 @@ class AppContainer extends Component { // Refresh sandbox page if (this.props.page === PAGE_SANDBOX) { if (this.props.sandboxName) { this.checkScenarioState(); this.refreshScenario(); this.refreshMap(); // Only update while scenario is running Loading Loading @@ -597,6 +602,7 @@ class AppContainer extends Component { this.props.changeSignInStatus(STATUS_SIGNED_OUT); this.props.changeSandboxName(''); this.updateScenario(null); this.props.changeTerminationInProgressCount(-1); this.props.changeActivationInProgressCount(-1); this.props.changeActivationInProgressScenarioName(''); this.props.changeMap({ueList: [], poaList: [], computeList: []}); Loading @@ -620,7 +626,7 @@ class AppContainer extends Component { * @param {module:model/GetStates} data The data returned by the service call. * @param {String} response The complete HTTP response. */ getStatesCb(error, data, response) { getSandboxStateCb(error, data) { if (error) { if (error.status === StatusCodes.UNAUTHORIZED) { this.stopSandboxCreationTimer(); Loading @@ -629,11 +635,13 @@ class AppContainer extends Component { } return; } for (var i in response.body.podStatus) { if (response.body.podStatus[i].logicalState !== 'Running') { if (data) { for (var i in data.podStatus) { if (data.podStatus[i].logicalState !== 'Running') { return; } } } // all pods runnings, sandbox is ready this.stopSandboxCreationTimer(); Loading Loading @@ -662,7 +670,50 @@ class AppContainer extends Component { }; this.meepMonEngineApi.getStates(queryParams, (error, data, response) => { this.getStatesCb(error, data, response); this.getSandboxStateCb(error, data, response); }); } } /** * Callback function to receive the result of the getStates operation. * @callback module:api/MonEngineApi~getStatesCallback * @param {String} error Error message, if any. * @param {module:model/GetStates} data The data returned by the service call. * @param {String} response The complete HTTP response. */ getScenarioStateCb(error, data) { if (error) { return; } // Termination is complete when all pods are no longer in Running state if (data) { for (var i in data.podStatus) { if (data.podStatus[i].logicalState === 'Running') { return; } } } this.props.changeTerminationInProgressCount(-1); } checkScenarioState() { // Only check scenario state during termination if (this.props.terminationInProgressCount === -1) { return; } var sandboxName = this.props.sandboxName; if (sandboxName !== '') { var queryParams = { 'type': 'scenario', 'sandbox': sandboxName, 'long': false }; this.meepMonEngineApi.getStates(queryParams, (error, data, response) => { this.getScenarioStateCb(error, data, response); }); } } Loading Loading @@ -715,7 +766,7 @@ class AppContainer extends Component { * @param {module:model/Scenario} data The data returned by the service call. */ getActiveScenarioCb(error, data) { if ((error !== null) || (!data.deployment)) { if (error !== null) { if (error.status === 404) { this.updateScenario(null); this.props.changeApiTable(null); Loading @@ -726,6 +777,11 @@ class AppContainer extends Component { return; } // Validate scenario if (data && !data.deployment) { return; } // Store & Process deployed scenario if (this.props.activationInProgressCount === -1) { this.updateScenario(data); Loading Loading @@ -754,6 +810,39 @@ class AppContainer extends Component { ); } checkScenarioStatus() { // // Scenario pods // axios // .get(`${basepathMonEngine}/states?long=true&type=scenario&sandbox=${this.props.sandbox}`) // .then(res => { // var scenarioPodsPhases = res.data.podStatus; // // Add node ID // if (this.props.exec && this.props.exec.table && this.props.exec.table.entries) { // for (var i in scenarioPodsPhases) { // var scenarioPod = scenarioPodsPhases[i]; // var elem = getElemByName(this.props.exec.table.entries, scenarioPod.name); // scenarioPodsPhases[i].id = elem ? elem.id : ''; // } // } // this.props.changeScenarioPodsPhases(scenarioPodsPhases); // }) // .catch(() => { // this.props.changeScenarioPodsPhases([]); // }); // // Service maps // axios // .get(`${basepathSandboxCtrl}/active/serviceMaps`) // .then(res => { // var serviceMaps = res.data; // this.props.changeServiceMaps(serviceMaps); // }) // .catch(() => { // this.props.changeServiceMaps([]); // }); } syncUeButtons(mapUeList) { var nbHv = 0, nbLv = 0, nbZv = 0; var found = false; Loading Loading @@ -1175,6 +1264,9 @@ class AppContainer extends Component { decrementProgressCounters() { // Update counters if (this.props.terminationInProgressCount !== -1) { this.props.changeTerminationInProgressCount(this.props.terminationInProgressCount - 1); } if (this.props.activationInProgressCount !== -1) { this.props.changeActivationInProgressCount(this.props.activationInProgressCount - 1); } Loading Loading @@ -1287,9 +1379,9 @@ class AppContainer extends Component { }} mepNames={this.props.mepList} /> <NewsDialog title='MEC Sandbox Beta Features' open={this.props.currentDialog === DIALOG_NEWS} <VersionDialog title='MEC Sandbox Versions' open={this.props.currentDialog === DIALOG_VERSION} onClose={() => { this.closeDialog(); }} Loading Loading @@ -1362,6 +1454,7 @@ const mapStateToProps = state => { svcTable: state.sbox.svcTable.data, updateUeInProgressCount: state.ui.updateUeInProgressCount, updateAppInstancesInProgressCount: state.ui.updateAppInstancesInProgressCount, terminationInProgressCount: state.ui.terminationInProgressCount, activationInProgressCount: state.ui.activationInProgressCount, activationInProgressScenarioName: state.ui.activationInProgressScenarioName, map: state.sbox.map, Loading Loading @@ -1407,6 +1500,7 @@ const mapDispatchToProps = dispatch => { changeUpdateUeInProgressCount: count => dispatch(uiSandboxChangeUpdateUeInProgressCount(count)), changeUpdateAutomationInProgressCount: count => dispatch(uiSandboxChangeUpdateAutomationInProgressCount(count)), changeUpdateAppInstancesInProgressCount: count => dispatch(uiSandboxChangeUpdateAppInstancesInProgressCount(count)), changeTerminationInProgressCount: count => dispatch(uiSandboxChangeTerminationInProgressCount(count)), changeActivationInProgressCount: count => dispatch(uiSandboxChangeActivationInProgressCount(count)), changeActivationInProgressScenarioName: name => dispatch(uiSandboxChangeActivationInProgressScenarioName(name)) }; Loading js-apps/frontend/src/js/containers/sandbox/configuration-pane.js +222 −183 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import { uiSandboxChangeUpdateUeInProgressCount, uiSandboxChangeUpdateAutomationInProgressCount, uiSandboxChangeUpdateAppInstancesInProgressCount, uiSandboxChangeTerminationInProgressCount, uiSandboxChangeActivationInProgressCount, uiSandboxChangeActivationInProgressScenarioName } from '../../state/ui'; Loading Loading @@ -81,7 +82,8 @@ class ConfigPane extends Component { } } componentDidUpdate() { componentDidUpdate(prevProps) { // Get configured scenario if activated by another browser if (this.props.activationInProgressCount === -1) { if (this.props.scenario !== null && this.props.scenario.name !== NO_SCENARIO_NAME) { if (this.props.networkFileSelected !== this.props.scenario.name) { Loading @@ -89,6 +91,24 @@ class ConfigPane extends Component { } } } // On termination completion, check if a scenario requires activation if (this.props.terminationInProgressCount === -1 && this.props.terminationInProgressCount !== prevProps.terminationInProgressCount && this.props.networkFileSelected !== DEFAULT_NO_NETWORK_FILE_SELECTED) { this.props.activateApi.activateScenario(this.props.networkFileSelected, null, (error, data, response) => { this.activateScenarioCb(error, data, response); }); this.getScenario(this.props.networkFileSelected, true); } // Reset config pane if scenario was terminated by another source if (this.props.activationInProgressCount === -1) { if (this.props.scenario === null && prevProps.scenario !== null) { this.resetConfigPane(); this.resetApiPane(); } } } decrement(value) { Loading Loading @@ -160,6 +180,12 @@ class ConfigPane extends Component { this.updateAutomation(false); } terminateScenarioCb(error) { if (error) { return; } } setScenario(name) { // Reset config & api panes this.resetConfigPane(); Loading @@ -172,48 +198,29 @@ class ConfigPane extends Component { if (name !== DEFAULT_NO_NETWORK_FILE_SELECTED) { if (this.props.scenario) { //if a scenario already is running this.props.changeActivationInProgressCount(5); this.terminateAndActivateScenario(name); this.props.changeActivationInProgressCount(10); // Terminate running scenario before acivating new scenario // NOTE: new scenario will be activated when old one has been fully removed this.props.changeTerminationInProgressCount(5); this.props.activateApi.terminateScenario((error, data, response) => { this.terminateScenarioCb(error, data, response); }); } else { //if no scenario running // Activate scenario immediately if no scenario running this.props.activateApi.activateScenario(name, null, (error, data, response) => { this.activateScenarioCb(error, data, response); }); this.getScenario(name, true); } } else { //scenario termination // Terminate scenario this.props.changeTerminationInProgressCount(5); this.props.activateApi.terminateScenario((error, data, response) => { this.terminateScenarioCb(error, data, response); }); } } terminateAndActivateScenarioCb(error) { if (error) { return; } this.props.activateApi.activateScenario(this.name, null, (error, data, response) => { this.activateScenarioCb(error, data, response); }); this.getScenario(this.name, true); } terminateAndActivateScenario(name) { this.name = name; this.props.activateApi.terminateScenario((error, data, response) => { this.terminateAndActivateScenarioCb(error, data, response); }); } terminateScenarioCb(error) { this.activationInProgress = false; if (error) { return; } } /** * Callback function to receive the result of the getScenario operation. * @callback module:api/ScenarioExecutionApi~getScenarioCallback Loading Loading @@ -449,7 +456,9 @@ class ConfigPane extends Component { return null; } const configEnabled = (this.props.networkFileSelected !== DEFAULT_NO_NETWORK_FILE_SELECTED) ? true : false; const terminationInProgress = (this.props.terminationInProgressCount !== -1) ? true : false; const networkFileSelected = (this.props.networkFileSelected !== DEFAULT_NO_NETWORK_FILE_SELECTED) ? true : false; const configEnabled = (networkFileSelected && !terminationInProgress) ? true : false; const networkFileSelectionEnabled = (this.props.activationInProgressCount === -1) ? true : false; const maxNbStationaryUe = (this.props.stationaryUeList) ? this.props.stationaryUeList.length : 0; const maxNbLowVelocityUe = (this.props.lowVelocityUeList) ? this.props.lowVelocityUeList.length : 0; Loading @@ -470,7 +479,7 @@ class ConfigPane extends Component { <Typography theme="primary" use="headline6">Configuration</Typography> </div> <Grid style={{ marginBottom: 30}}> <Grid> <GridCell span={12}> <Select title="Select the network to simulate" Loading @@ -489,8 +498,9 @@ class ConfigPane extends Component { </GridCell> </Grid> {!terminationInProgress && <div style={!configEnabled ? {pointerEvents: 'none', opacity: '0.3', marginLeft: 5} : { marginLeft: 5 }}> <Grid style={{ marginBottom: 20}}> <Grid style={{ marginTop: 30, marginBottom: 20}}> <GridCell span={5}> <Typography use="body1">Pause</Typography> </GridCell> Loading Loading @@ -648,6 +658,33 @@ class ConfigPane extends Component { <AppInstanceTable/> </div> } {networkFileSelected && terminationInProgress && <Grid> <GridCell span={12}> <div style={{ marginTop: 15 }}> <Typography theme="primary" use="body1"> Network activation in progress...<br/> This operation takes a few seconds. <br/> </Typography> </div> </GridCell> </Grid> } {!networkFileSelected && terminationInProgress && <Grid> <GridCell span={12}> <div style={{ marginTop: 15 }}> <Typography theme="primary" use="body1"> Network termination in progress...<br/> This operation takes a few seconds. </Typography> </div> </GridCell> </Grid> } <div hidden={!this.props.currentAlert} style={{ width: '100%', borderTop: '2px solid #e4e4e4', marginTop: 30, marginBottom: 10}} /> Loading Loading @@ -731,6 +768,7 @@ const mapStateToProps = state => { appInstanceTable: state.sbox.appInstanceTable.data, scenario: state.sbox.scenario, appInstancesSelected: state.ui.appInstancesSelected, terminationInProgressCount: state.ui.terminationInProgressCount, activationInProgressCount: state.ui.activationInProgressCount, activationInProgressScenarioName: state.ui.activationInProgressScenarioName }; Loading @@ -739,6 +777,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)), changeTerminationInProgressCount: count => dispatch(uiSandboxChangeTerminationInProgressCount(count)), changeActivationInProgressCount: count => dispatch(uiSandboxChangeActivationInProgressCount(count)), changeActivationInProgressScenarioName: name => dispatch(uiSandboxChangeActivationInProgressScenarioName(name)), changeUpdateUeInProgressCount: count => dispatch(uiSandboxChangeUpdateUeInProgressCount(count)), Loading Loading
js-apps/frontend/src/js/app-constants.js +5 −1 Original line number Diff line number Diff line Loading @@ -2,6 +2,10 @@ * Copyright (c) 2020 ETSI. All rights reserved. */ // Version export const MEC_SANDBOX_VERSION = 'v1.6'; // Host export const HOST_PATH = location.origin; // Pages Loading Loading @@ -57,7 +61,7 @@ export const DIALOG_SESSION_TERMINATED = 'DIALOG_SESSION_TERMINATED'; export const DIALOG_HELP_GETTING_STARTED = 'DIALOG_HELP_GETTING_STARTED'; export const DIALOG_CONFIRM_DELETE_APP = 'DIALOG_CONFIRM_DELETE_APP'; export const DIALOG_CREATE_NEW_APP = 'DIALOG_CREATE_NEW_APP'; export const DIALOG_NEWS = 'DIALOG_NEWS'; export const DIALOG_VERSION = 'DIALOG_VERSION'; // Alert Types export const ALERT_DEGRADED_NETWORK = 'ALERT_DEGRADED_NETWORK'; Loading
js-apps/frontend/src/js/components/dialogs/news-dialog.jsdeleted 100644 → 0 +0 −75 Original line number Diff line number Diff line /* * Copyright (c) 2020 ETSI. All rights reserved. */ import React, { Component } from 'react'; import BasicDialog from './basic-dialog'; import { Typography } from '@rmwc/typography'; import { WIKI_HELP_URL, WIKI_DISCUSSION_BOARD_URL } from '../../app-constants'; class NewsDialog extends Component { constructor(props) { super(props); } render() { return ( <BasicDialog title={this.props.title} open={this.props.open} onSubmit={this.props.onClose} onClose={this.props.onClose} submitLabel = {'Ok'} > <div style={styles.text}> <Typography theme="primary" use="body1"> <p><b>November 2021</b></p> </Typography> <Typography use="body1"> WebSocket support for subscriptions per MEC009: <ul> <li>MEC028 - WLAN Access Information API (v2.2.1)</li> </ul> </Typography> <Typography theme="primary" use="body1"> <p><b>October 2021</b></p> </Typography> <Typography use="body1"> The following APIs are supported as Beta features: <ul> <li>MEC011 - Edge Platform Application Enablement (v2.1.1)</li> <li>MEC021 - Application Mobility Service API (v2.1.1)</li> </ul> <p> Usage details are available in the <a href={WIKI_HELP_URL} target="_blank">MEC Sandbox Wiki</a><br/> Questions & feedback are appreciated on the <a href={WIKI_DISCUSSION_BOARD_URL} target="_blank">Slack Discussion Board</a> </p> <p> Thank you in advance for your collaboration in trying out these new features. </p> <p style={styles.signature}> The MEC Sandbox Team </p> </Typography> </div> </BasicDialog> ); } } const styles = { text: { // color: 'black', marginLeft: 10, marginRight: 15 }, signature: { textAlign: 'right' } }; export default NewsDialog;
js-apps/frontend/src/js/components/dialogs/version-dialog.js 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (c) 2020 ETSI. All rights reserved. */ import React, { Component } from 'react'; import BasicDialog from './basic-dialog'; import { Typography } from '@rmwc/typography'; class VersionDialog extends Component { constructor(props) { super(props); } render() { return ( <BasicDialog title={this.props.title} open={this.props.open} onSubmit={this.props.onClose} onClose={this.props.onClose} submitLabel = {'Ok'} > <div style={styles.text}> <Typography theme="primary" use="subtitle1"><p><b>v1.6 •</b> 2021-12-16</p></Typography> <Typography use="body1"> Final STF599 update: <ul> <li>AdvantEDGE baseline upgrade to v1.8.1</li> <li>General maintenance fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5.3 (beta) •</b> 2021-11-22</p></Typography> <Typography use="body1"> Maintenance Release: <ul> <li>Service IDs displayed in frontend</li> <li>Garbage collector for internal databases</li> <li>User-persistent sandbox names</li> <li>MEC Service bug fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5.2 (beta) •</b> 2021-11-08</p></Typography> <Typography use="body1"> MEC028 WebSockets: <ul> <li>MEC028 subscriptions - support for MEC009 WebSocket fallback pattern</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5.1 (beta) •</b> 2021-10-30</p></Typography> <Typography use="body1"> MEC API Maintenance: <ul> <li>MEC Service bug fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.5 (beta) •</b> 2021-09-30</p></Typography> <Typography use="body1"> New MEC APIs: <ul> <li>MEC011 - Edge Platform Application Enablement</li> <li>MEC021 - Application Mobility Service</li> <li>Dual MEP network to showcase new APIs</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.4.1 •</b> 2021-08-06</p></Typography> <Typography use="body1"> MEC Service Maintenance: <ul> <li>MEC Service bug fixes</li> <li>Beta tags removed from API endpoints</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.4 (beta) •</b> 2021-07-20</p></Typography> <Typography use="body1"> MEC Service Enhancements: <ul> <li>MEC012, MEC013 & MEC028 support for all API endpoints</li> <li>MEC Service bug fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.3 •</b> 2021-06-03</p></Typography> <Typography use="body1"> Long-term Sandbox Analytics Storage: <ul> <li>Metrics object storage deployment</li> <li>Thanos integration for metrics data long-term storage</li> <li>Maintenance fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.2 •</b> 2021-05-11</p></Typography> <Typography use="body1"> MEC Sandbox VM Upgrade: <ul> <li>CPU/Memory upgrade</li> <li>Maintenance fixes</li> </ul> </Typography> <Typography theme="primary" use="subtitle1"><p><b>v1.1 •</b> 2021-03-23</p></Typography> <Typography use="body1"> Monitoring & Analytics: <ul> <li>Session statistics</li> <li>MEC Service & Platform API metrics</li> <li>Alerting framework</li> <li>Maintenance fixes</li> </ul> </Typography> </div> </BasicDialog> ); } } const styles = { text: { // color: 'black', marginLeft: 10, marginRight: 15 }, signature: { textAlign: 'right' } }; export default VersionDialog;
js-apps/frontend/src/js/containers/app-container.js +105 −11 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ import SessionTerminatedDialog from '../components/dialogs/session-terminated-di import HelpGettingStartedDialog from '../components/dialogs/help-getting-started-dialog'; import ConfirmDeleteAppDialog from '../components/dialogs/confirm-delete-app-dialog'; import CreateNewAppDialog from '../components/dialogs/create-new-app-dialog'; import NewsDialog from '../components/dialogs/news-dialog'; import VersionDialog from '../components/dialogs/version-dialog'; import * as meepAuthSvcRestApiClient from '../../../../../js-packages/meep-auth-svc-client/src/index.js'; import * as meepPlatformCtrlRestApiClient from '../../../../../js-packages/meep-platform-ctrl-client/src/index.js'; Loading @@ -45,7 +45,7 @@ import { DIALOG_HELP_GETTING_STARTED, DIALOG_CONFIRM_DELETE_APP, DIALOG_CREATE_NEW_APP, DIALOG_NEWS, DIALOG_VERSION, ALERT_DEGRADED_NETWORK, STATUS_SIGNED_IN, STATUS_SIGNING_IN, Loading Loading @@ -86,6 +86,7 @@ import { uiSandboxChangeUpdateUeInProgressCount, uiSandboxChangeUpdateAutomationInProgressCount, uiSandboxChangeUpdateAppInstancesInProgressCount, uiSandboxChangeTerminationInProgressCount, uiSandboxChangeActivationInProgressCount, uiSandboxChangeActivationInProgressScenarioName } from '../state/ui'; Loading Loading @@ -169,6 +170,9 @@ class AppContainer extends Component { this.meepMonEngineApi = new meepMonEngineRestApiClient.PodStatesApi(); // Initialize undefined states if (this.props.terminationInProgressCount === undefined) { this.props.changeTerminationInProgressCount(-1); } if (this.props.activationInProgressCount === undefined) { this.props.changeActivationInProgressCount(-1); } Loading Loading @@ -301,6 +305,7 @@ class AppContainer extends Component { // Refresh sandbox page if (this.props.page === PAGE_SANDBOX) { if (this.props.sandboxName) { this.checkScenarioState(); this.refreshScenario(); this.refreshMap(); // Only update while scenario is running Loading Loading @@ -597,6 +602,7 @@ class AppContainer extends Component { this.props.changeSignInStatus(STATUS_SIGNED_OUT); this.props.changeSandboxName(''); this.updateScenario(null); this.props.changeTerminationInProgressCount(-1); this.props.changeActivationInProgressCount(-1); this.props.changeActivationInProgressScenarioName(''); this.props.changeMap({ueList: [], poaList: [], computeList: []}); Loading @@ -620,7 +626,7 @@ class AppContainer extends Component { * @param {module:model/GetStates} data The data returned by the service call. * @param {String} response The complete HTTP response. */ getStatesCb(error, data, response) { getSandboxStateCb(error, data) { if (error) { if (error.status === StatusCodes.UNAUTHORIZED) { this.stopSandboxCreationTimer(); Loading @@ -629,11 +635,13 @@ class AppContainer extends Component { } return; } for (var i in response.body.podStatus) { if (response.body.podStatus[i].logicalState !== 'Running') { if (data) { for (var i in data.podStatus) { if (data.podStatus[i].logicalState !== 'Running') { return; } } } // all pods runnings, sandbox is ready this.stopSandboxCreationTimer(); Loading Loading @@ -662,7 +670,50 @@ class AppContainer extends Component { }; this.meepMonEngineApi.getStates(queryParams, (error, data, response) => { this.getStatesCb(error, data, response); this.getSandboxStateCb(error, data, response); }); } } /** * Callback function to receive the result of the getStates operation. * @callback module:api/MonEngineApi~getStatesCallback * @param {String} error Error message, if any. * @param {module:model/GetStates} data The data returned by the service call. * @param {String} response The complete HTTP response. */ getScenarioStateCb(error, data) { if (error) { return; } // Termination is complete when all pods are no longer in Running state if (data) { for (var i in data.podStatus) { if (data.podStatus[i].logicalState === 'Running') { return; } } } this.props.changeTerminationInProgressCount(-1); } checkScenarioState() { // Only check scenario state during termination if (this.props.terminationInProgressCount === -1) { return; } var sandboxName = this.props.sandboxName; if (sandboxName !== '') { var queryParams = { 'type': 'scenario', 'sandbox': sandboxName, 'long': false }; this.meepMonEngineApi.getStates(queryParams, (error, data, response) => { this.getScenarioStateCb(error, data, response); }); } } Loading Loading @@ -715,7 +766,7 @@ class AppContainer extends Component { * @param {module:model/Scenario} data The data returned by the service call. */ getActiveScenarioCb(error, data) { if ((error !== null) || (!data.deployment)) { if (error !== null) { if (error.status === 404) { this.updateScenario(null); this.props.changeApiTable(null); Loading @@ -726,6 +777,11 @@ class AppContainer extends Component { return; } // Validate scenario if (data && !data.deployment) { return; } // Store & Process deployed scenario if (this.props.activationInProgressCount === -1) { this.updateScenario(data); Loading Loading @@ -754,6 +810,39 @@ class AppContainer extends Component { ); } checkScenarioStatus() { // // Scenario pods // axios // .get(`${basepathMonEngine}/states?long=true&type=scenario&sandbox=${this.props.sandbox}`) // .then(res => { // var scenarioPodsPhases = res.data.podStatus; // // Add node ID // if (this.props.exec && this.props.exec.table && this.props.exec.table.entries) { // for (var i in scenarioPodsPhases) { // var scenarioPod = scenarioPodsPhases[i]; // var elem = getElemByName(this.props.exec.table.entries, scenarioPod.name); // scenarioPodsPhases[i].id = elem ? elem.id : ''; // } // } // this.props.changeScenarioPodsPhases(scenarioPodsPhases); // }) // .catch(() => { // this.props.changeScenarioPodsPhases([]); // }); // // Service maps // axios // .get(`${basepathSandboxCtrl}/active/serviceMaps`) // .then(res => { // var serviceMaps = res.data; // this.props.changeServiceMaps(serviceMaps); // }) // .catch(() => { // this.props.changeServiceMaps([]); // }); } syncUeButtons(mapUeList) { var nbHv = 0, nbLv = 0, nbZv = 0; var found = false; Loading Loading @@ -1175,6 +1264,9 @@ class AppContainer extends Component { decrementProgressCounters() { // Update counters if (this.props.terminationInProgressCount !== -1) { this.props.changeTerminationInProgressCount(this.props.terminationInProgressCount - 1); } if (this.props.activationInProgressCount !== -1) { this.props.changeActivationInProgressCount(this.props.activationInProgressCount - 1); } Loading Loading @@ -1287,9 +1379,9 @@ class AppContainer extends Component { }} mepNames={this.props.mepList} /> <NewsDialog title='MEC Sandbox Beta Features' open={this.props.currentDialog === DIALOG_NEWS} <VersionDialog title='MEC Sandbox Versions' open={this.props.currentDialog === DIALOG_VERSION} onClose={() => { this.closeDialog(); }} Loading Loading @@ -1362,6 +1454,7 @@ const mapStateToProps = state => { svcTable: state.sbox.svcTable.data, updateUeInProgressCount: state.ui.updateUeInProgressCount, updateAppInstancesInProgressCount: state.ui.updateAppInstancesInProgressCount, terminationInProgressCount: state.ui.terminationInProgressCount, activationInProgressCount: state.ui.activationInProgressCount, activationInProgressScenarioName: state.ui.activationInProgressScenarioName, map: state.sbox.map, Loading Loading @@ -1407,6 +1500,7 @@ const mapDispatchToProps = dispatch => { changeUpdateUeInProgressCount: count => dispatch(uiSandboxChangeUpdateUeInProgressCount(count)), changeUpdateAutomationInProgressCount: count => dispatch(uiSandboxChangeUpdateAutomationInProgressCount(count)), changeUpdateAppInstancesInProgressCount: count => dispatch(uiSandboxChangeUpdateAppInstancesInProgressCount(count)), changeTerminationInProgressCount: count => dispatch(uiSandboxChangeTerminationInProgressCount(count)), changeActivationInProgressCount: count => dispatch(uiSandboxChangeActivationInProgressCount(count)), changeActivationInProgressScenarioName: name => dispatch(uiSandboxChangeActivationInProgressScenarioName(name)) }; Loading
js-apps/frontend/src/js/containers/sandbox/configuration-pane.js +222 −183 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import { uiSandboxChangeUpdateUeInProgressCount, uiSandboxChangeUpdateAutomationInProgressCount, uiSandboxChangeUpdateAppInstancesInProgressCount, uiSandboxChangeTerminationInProgressCount, uiSandboxChangeActivationInProgressCount, uiSandboxChangeActivationInProgressScenarioName } from '../../state/ui'; Loading Loading @@ -81,7 +82,8 @@ class ConfigPane extends Component { } } componentDidUpdate() { componentDidUpdate(prevProps) { // Get configured scenario if activated by another browser if (this.props.activationInProgressCount === -1) { if (this.props.scenario !== null && this.props.scenario.name !== NO_SCENARIO_NAME) { if (this.props.networkFileSelected !== this.props.scenario.name) { Loading @@ -89,6 +91,24 @@ class ConfigPane extends Component { } } } // On termination completion, check if a scenario requires activation if (this.props.terminationInProgressCount === -1 && this.props.terminationInProgressCount !== prevProps.terminationInProgressCount && this.props.networkFileSelected !== DEFAULT_NO_NETWORK_FILE_SELECTED) { this.props.activateApi.activateScenario(this.props.networkFileSelected, null, (error, data, response) => { this.activateScenarioCb(error, data, response); }); this.getScenario(this.props.networkFileSelected, true); } // Reset config pane if scenario was terminated by another source if (this.props.activationInProgressCount === -1) { if (this.props.scenario === null && prevProps.scenario !== null) { this.resetConfigPane(); this.resetApiPane(); } } } decrement(value) { Loading Loading @@ -160,6 +180,12 @@ class ConfigPane extends Component { this.updateAutomation(false); } terminateScenarioCb(error) { if (error) { return; } } setScenario(name) { // Reset config & api panes this.resetConfigPane(); Loading @@ -172,48 +198,29 @@ class ConfigPane extends Component { if (name !== DEFAULT_NO_NETWORK_FILE_SELECTED) { if (this.props.scenario) { //if a scenario already is running this.props.changeActivationInProgressCount(5); this.terminateAndActivateScenario(name); this.props.changeActivationInProgressCount(10); // Terminate running scenario before acivating new scenario // NOTE: new scenario will be activated when old one has been fully removed this.props.changeTerminationInProgressCount(5); this.props.activateApi.terminateScenario((error, data, response) => { this.terminateScenarioCb(error, data, response); }); } else { //if no scenario running // Activate scenario immediately if no scenario running this.props.activateApi.activateScenario(name, null, (error, data, response) => { this.activateScenarioCb(error, data, response); }); this.getScenario(name, true); } } else { //scenario termination // Terminate scenario this.props.changeTerminationInProgressCount(5); this.props.activateApi.terminateScenario((error, data, response) => { this.terminateScenarioCb(error, data, response); }); } } terminateAndActivateScenarioCb(error) { if (error) { return; } this.props.activateApi.activateScenario(this.name, null, (error, data, response) => { this.activateScenarioCb(error, data, response); }); this.getScenario(this.name, true); } terminateAndActivateScenario(name) { this.name = name; this.props.activateApi.terminateScenario((error, data, response) => { this.terminateAndActivateScenarioCb(error, data, response); }); } terminateScenarioCb(error) { this.activationInProgress = false; if (error) { return; } } /** * Callback function to receive the result of the getScenario operation. * @callback module:api/ScenarioExecutionApi~getScenarioCallback Loading Loading @@ -449,7 +456,9 @@ class ConfigPane extends Component { return null; } const configEnabled = (this.props.networkFileSelected !== DEFAULT_NO_NETWORK_FILE_SELECTED) ? true : false; const terminationInProgress = (this.props.terminationInProgressCount !== -1) ? true : false; const networkFileSelected = (this.props.networkFileSelected !== DEFAULT_NO_NETWORK_FILE_SELECTED) ? true : false; const configEnabled = (networkFileSelected && !terminationInProgress) ? true : false; const networkFileSelectionEnabled = (this.props.activationInProgressCount === -1) ? true : false; const maxNbStationaryUe = (this.props.stationaryUeList) ? this.props.stationaryUeList.length : 0; const maxNbLowVelocityUe = (this.props.lowVelocityUeList) ? this.props.lowVelocityUeList.length : 0; Loading @@ -470,7 +479,7 @@ class ConfigPane extends Component { <Typography theme="primary" use="headline6">Configuration</Typography> </div> <Grid style={{ marginBottom: 30}}> <Grid> <GridCell span={12}> <Select title="Select the network to simulate" Loading @@ -489,8 +498,9 @@ class ConfigPane extends Component { </GridCell> </Grid> {!terminationInProgress && <div style={!configEnabled ? {pointerEvents: 'none', opacity: '0.3', marginLeft: 5} : { marginLeft: 5 }}> <Grid style={{ marginBottom: 20}}> <Grid style={{ marginTop: 30, marginBottom: 20}}> <GridCell span={5}> <Typography use="body1">Pause</Typography> </GridCell> Loading Loading @@ -648,6 +658,33 @@ class ConfigPane extends Component { <AppInstanceTable/> </div> } {networkFileSelected && terminationInProgress && <Grid> <GridCell span={12}> <div style={{ marginTop: 15 }}> <Typography theme="primary" use="body1"> Network activation in progress...<br/> This operation takes a few seconds. <br/> </Typography> </div> </GridCell> </Grid> } {!networkFileSelected && terminationInProgress && <Grid> <GridCell span={12}> <div style={{ marginTop: 15 }}> <Typography theme="primary" use="body1"> Network termination in progress...<br/> This operation takes a few seconds. </Typography> </div> </GridCell> </Grid> } <div hidden={!this.props.currentAlert} style={{ width: '100%', borderTop: '2px solid #e4e4e4', marginTop: 30, marginBottom: 10}} /> Loading Loading @@ -731,6 +768,7 @@ const mapStateToProps = state => { appInstanceTable: state.sbox.appInstanceTable.data, scenario: state.sbox.scenario, appInstancesSelected: state.ui.appInstancesSelected, terminationInProgressCount: state.ui.terminationInProgressCount, activationInProgressCount: state.ui.activationInProgressCount, activationInProgressScenarioName: state.ui.activationInProgressScenarioName }; Loading @@ -739,6 +777,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)), changeTerminationInProgressCount: count => dispatch(uiSandboxChangeTerminationInProgressCount(count)), changeActivationInProgressCount: count => dispatch(uiSandboxChangeActivationInProgressCount(count)), changeActivationInProgressScenarioName: name => dispatch(uiSandboxChangeActivationInProgressScenarioName(name)), changeUpdateUeInProgressCount: count => dispatch(uiSandboxChangeUpdateUeInProgressCount(count)), Loading