aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/apps/configurationApp/src
diff options
context:
space:
mode:
authorAijana Schumann <aijana.schumann@highstreet-technologies.com>2021-08-04 11:59:18 +0200
committerAijana Schumann <aijana.schumann@highstreet-technologies.com>2021-08-04 16:06:05 +0200
commit437f67407aece6f7aed8e989638b0d64075f0c0a (patch)
tree53e9e336cd8544edf8a06c889e33f5b9c98fe083 /sdnr/wt/odlux/apps/configurationApp/src
parent1c4995eb199437e9c86336efff9972f2049e1532 (diff)
Update ODLUX
Add various updates and bugfixes to NetworkMap, Configuration, LinkCalculation and ConnectApp Issue-ID: CCSDK-3414 Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com> Change-Id: I6ea5c3a9d6ccbe9c450da43220654a53fd2f262b Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Diffstat (limited to 'sdnr/wt/odlux/apps/configurationApp/src')
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts26
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/index.html6
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts4
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx2
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts123
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx14
-rw-r--r--sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts47
7 files changed, 136 insertions, 86 deletions
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
index f80fbfc4d..b5dd310bc 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
+++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
@@ -52,26 +52,20 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc
dispatch(new UpdateDeviceDescription("", {}, []));
dispatch(new SetCollectingSelectionData(true));
- const { avaliableCapabilities, unavaliableCapabilities } = await restService.getCapabilitiesByMoutId(nodeId);
-
- if (!avaliableCapabilities || avaliableCapabilities.length <= 0) {
+ const { availableCapabilities, unavailableCapabilities, importOnlyModules } = await restService.getCapabilitiesByMountId(nodeId);
+
+ if (!availableCapabilities || availableCapabilities.length <= 0) {
throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`);
}
-
- const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i;
-
- const parser = new YangParser(unavaliableCapabilities?.map(cap => {
- const capMatch = cap && capParser.exec(cap.capability);
- return { capability:capMatch && capMatch[2] || '', failureReason: cap.failureReason };
- }) || undefined);
-
- for (let i = 0; i < avaliableCapabilities.length; ++i){
- const capRaw = avaliableCapabilities[i];
- const capMatch = capRaw && capParser.exec(capRaw.capability);
+
+ const parser = new YangParser(unavailableCapabilities || undefined, importOnlyModules || undefined);
+
+ for (let i = 0; i < availableCapabilities.length; ++i){
+ const capRaw = availableCapabilities[i];
try {
- capMatch && await parser.addCapability(capMatch[2], capMatch[1]);
+ await parser.addCapability(capRaw.capability, capRaw.version);
} catch (err) {
- console.error(`Error in ${capMatch && capMatch[2]} ${capMatch && capMatch[1]}`, err);
+ console.error(`Error in ${capRaw.capability} ${capRaw.version}`, err);
}
}
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/index.html b/sdnr/wt/odlux/apps/configurationApp/src/index.html
index 78fff78c5..4a0496bff 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/index.html
+++ b/sdnr/wt/odlux/apps/configurationApp/src/index.html
@@ -15,11 +15,13 @@
<script type="text/javascript" src="./config.js"></script>
<script>
// run the application
- require(["app", "connectApp", "maintenanceApp", "configurationApp"], function (app, connectApp, maintenanceApp, configurationApp) {
+ require(["app", "connectApp", "maintenanceApp", "configurationApp", "faultApp"], function (app, connectApp, maintenanceApp, configurationApp, faultApp) {
connectApp.register();
configurationApp.register();
maintenanceApp.register();
- app("./app.tsx").configureApplication({ authentication:"oauth", enablePolicy: true,});
+ faultApp.register();
+ // app("./app.tsx").configureApplication({ authentication:"oauth", enablePolicy: true,});
+ app("./app.tsx").configureApplication({ authentication:"basic", enablePolicy: false,});
app("./app.tsx").runApplication();
});
</script>
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts
index e4ab6f59f..10f538c2e 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts
+++ b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts
@@ -17,12 +17,12 @@
*/
import { ViewElement, ViewSpecification } from "./uiModels";
-import { StepLabel } from "@material-ui/core";
export enum ModuleState {
stable,
instable,
- unabaliabe,
+ importOnly,
+ unavailable,
}
export type Token = {
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx
index 3bc0e3968..3b9baa657 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx
+++ b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx
@@ -88,7 +88,7 @@ const ConfigurationApplicationRouteAdapter = connect(undefined, mapDisp)((props:
// result += `${indention} [${view.canEdit ? 'rw' : 'ro'}] ${view.ns}:${view.name} ${ds.displayMode === DisplayModeType.displayAsList ? '[LIST]' : ''}\r\n`;
result += Object.keys(view.elements).reduce((acc, cur) => {
const elm = view.elements[cur];
- acc += `${indention} [${elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === "object" && elm.isList ? `as LIST with KEY [${elm.key}]` : ""}\r\n`;
+ acc += `${indention} [${elm.uiType === "rpc" ? "x" : elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === "object" && elm.isList ? `as LIST with KEY [${elm.key}]` : ""}\r\n`;
// acc += `${indention} +${elm.mandatory ? "mandetory" : "none"} - ${elm.path} \r\n`;
switch (elm.uiType) {
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts
index bdef64cf2..02060ef12 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts
+++ b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts
@@ -21,52 +21,101 @@ import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/u
import { NetworkElementConnection } from "../models/networkElementConnection";
+type ImportOnlyResponse = {
+ "ietf-yang-library:yang-library": {
+ "module-set": {
+ "import-only-module": {
+ "name": string,
+ "revision": string,
+ }[],
+ }[],
+ },
+}
+
+
type CapabilityResponse = {
- "network-topology:node": {
- "node-id": string,
- "netconf-node-topology:available-capabilities": {
- "available-capability": {
- "capability-origin": string,
- "capability": string,
- }[]
- },
- "netconf-node-topology:unavailable-capabilities": {
- "unavailable-capability": {
- "capability": string,
- "failure-reason": string,
- }[]
- }
- }[]
+ "network-topology:node": {
+ "node-id": string,
+ "netconf-node-topology:available-capabilities": {
+ "available-capability": {
+ "capability-origin": string,
+ "capability": string,
+ }[]
+ },
+ "netconf-node-topology:unavailable-capabilities": {
+ "unavailable-capability": {
+ "capability": string,
+ "failure-reason": string,
+ }[]
+ }
+ }[]
}
-type CapabilityAnswer = {
- avaliableCapabilities: {
- capabilityOrigin: string,
- capability: string
- }[] | null ,
- unavaliableCapabilities: {
- failureReason: string,
- capability: string
- }[] | null ,
+type CapabilityAnswer = {
+ availableCapabilities: {
+ capabilityOrigin: string,
+ capability: string,
+ version: string,
+ }[] | null,
+ unavailableCapabilities: {
+ failureReason: string,
+ capability: string,
+ version: string,
+ }[] | null,
+ importOnlyModules: {
+ name: string,
+ revision: string,
+ }[] | null
}
+const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i;
+
class RestService {
public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId;
- public async getCapabilitiesByMoutId(nodeId: string): Promise<CapabilityAnswer> {
+ public async getImportOnlyModules(nodeId: string): Promise<{ name: string, revision: string }[]> {
+ const path = `${this.getNetworkElementUri(nodeId)}/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig&fields=module-set(import-only-module(name;revision))`;
+ const importOnlyResult = await requestRest<ImportOnlyResponse>(path, { method: "GET" });
+ const importOnlyModules = importOnlyResult
+ ? importOnlyResult["ietf-yang-library:yang-library"]["module-set"][0]["import-only-module"]
+ : [];
+ return importOnlyModules;
+ }
+
+ public async getCapabilitiesByMountId(nodeId: string): Promise<CapabilityAnswer> {
const path = this.getNetworkElementUri(nodeId);
const capabilitiesResult = await requestRest<CapabilityResponse>(path, { method: "GET" });
- const avaliableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 &&
- capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"] &&
- capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"] &&
- capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [];
-
- const unavaliableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 &&
- capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"] &&
- capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"] &&
- capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || []
-
- return { avaliableCapabilities, unavaliableCapabilities };
+ const availableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 &&
+ (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"] &&
+ capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"] &&
+ capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [])
+ .map(cap => {
+ const capMatch = cap && capParser.exec(cap.capability);
+ return capMatch ? {
+ capabilityOrigin: cap.capabilityOrigin,
+ capability: capMatch && capMatch[2] || '',
+ version: capMatch && capMatch[1] || '',
+ } : null ;
+ }).filter((cap) => cap != null) || [] as any;
+
+ const unavailableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 &&
+ (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"] &&
+ capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"] &&
+ capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [])
+ .map(cap => {
+ const capMatch = cap && capParser.exec(cap.capability);
+ return capMatch ? {
+ failureReason: cap.failureReason,
+ capability: capMatch && capMatch[2] || '',
+ version: capMatch && capMatch[1] || '',
+ } : null ;
+ }).filter((cap) => cap != null) || [] as any;
+
+ const importOnlyModules = availableCapabilities && availableCapabilities.findIndex((ac: {capability: string }) => ac.capability && ac.capability.toLowerCase() === "ietf-yang-library") > -1
+ ? await this.getImportOnlyModules(nodeId)
+ : null;
+
+ return { availableCapabilities, unavailableCapabilities, importOnlyModules };
}
public async getMountedNetworkElementByMountId(nodeId: string): Promise<NetworkElementConnection | null> {
@@ -80,7 +129,7 @@ class RestService {
return networkElementResult && networkElementResult["data-provider:output"] && networkElementResult["data-provider:output"].data &&
networkElementResult["data-provider:output"].data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null;
}
-
+
/** Reads the config data by restconf path.
* @param path The restconf path to be used for read.
* @returns The data.
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
index db426e814..8d0e19246 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
+++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
@@ -578,9 +578,11 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
private renderUIViewList(listSpecification: ViewSpecification, dataPath: string, listKeyProperty: string, apiDocPath: string, listData: { [key: string]: any }[]) {
const listElements = listSpecification.elements;
- const apiDocPathCreate = apiDocPath ? `${location.origin}${apiDocPath
+ const apiDocPathCreate = apiDocPath ? `${location.origin}${apiDocPath
.replace("$$$standard$$$", "topology-netconfnode%20resources%20-%20RestConf%20RFC%208040")
- .replace("$$$action$$$", "put")}_${listKeyProperty.replace(/[\/=\-\:]/g, '_')}_` : undefined;
+ .replace("$$$action$$$", "put")}${listKeyProperty ? `_${listKeyProperty.replace(/[\/=\-\:]/g, '_')}_` : '' }` : undefined;
+
+ const config = listSpecification.config && listKeyProperty; // We can not configure a list with no key.
const navigate = (path: string) => {
this.props.history.push(`${this.props.match.url}${path}`);
@@ -593,7 +595,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
onClick: () => {
navigate("[]"); // empty key means new element
},
- disabled: !listSpecification.config,
+ disabled: !config,
};
const addWithApiDocElementAction = {
@@ -603,7 +605,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
onClick: () => {
window.open(apiDocPathCreate, '_blank');
},
- disabled: !listSpecification.config,
+ disabled: !config,
};
const { classes, removeElement } = this.props;
@@ -650,13 +652,13 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
}, []).concat([{
property: "Actions", disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (({ rowData }) => {
return (
- <DeleteIconWithConfirmation disabled={!listSpecification.config} rowData={rowData} onReload={() => this.props.vPath && this.props.reloadView(this.props.vPath)} />
+ <DeleteIconWithConfirmation disabled={!config} rowData={rowData} onReload={() => this.props.vPath && this.props.reloadView(this.props.vPath)} />
);
})
}])
} onHandleClick={(ev, row) => {
ev.preventDefault();
- navigate(`[${encodeURIComponent(row[listKeyProperty])}]`);
+ listKeyProperty && navigate(`[${encodeURIComponent(row[listKeyProperty])}]`); // Do not navigate without key.
}} ></SelectElementTable>
);
}
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts
index 2d38976d5..c80bd4c84 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts
+++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts
@@ -286,7 +286,7 @@ export class YangParser {
public static ResolveStack = Symbol("ResolveStack");
- constructor(private _unavailableCapabilities: { failureReason: string; capability: string; }[] = []) {
+ constructor(private _unavailableCapabilities: { failureReason: string; capability: string; }[] = [], private _importOnlyModules: { name: string; revision: string; }[] = []) {
}
@@ -298,16 +298,16 @@ export class YangParser {
return this._views;
}
- public async addCapability(capability: string, version?: string) {
+ public async addCapability(capability: string, version?: string, parentImportOnlyModule?: boolean) {
// do not add twice
if (this._modules[capability]) {
- // console.warn(`Skipped capability: ${capability} since allready contained.` );
+ // console.warn(`Skipped capability: ${capability} since already contained.` );
return;
}
- // // do not add unavaliabe capabilities
+ // // do not add unavailable capabilities
// if (this._unavailableCapabilities.some(c => c.capability === capability)) {
- // // console.warn(`Skipped capability: ${capability} since it is marked as unavaliable.` );
+ // // console.warn(`Skipped capability: ${capability} since it is marked as unavailable.` );
// return;
// }
@@ -325,7 +325,8 @@ export class YangParser {
throw new Error(`Root element capability ${rootStatement.arg} does not requested ${capability}.`);
}
- const isUnavaliabe = this._unavailableCapabilities.some(c => c.capability === capability);
+ const isUnavailable = this._unavailableCapabilities.some(c => c.capability === capability);
+ const isImportOnly = parentImportOnlyModule === true || this._importOnlyModules.some(c => c.name === capability);
const module = this._modules[capability] = {
name: rootStatement.arg,
@@ -338,9 +339,11 @@ export class YangParser {
typedefs: {},
views: {},
elements: {},
- state: isUnavaliabe
- ? ModuleState.unabaliabe
- : ModuleState.stable,
+ state: isUnavailable
+ ? ModuleState.unavailable
+ : isImportOnly
+ ? ModuleState.importOnly
+ : ModuleState.stable,
};
await this.handleModule(module, rootStatement, capability);
@@ -406,7 +409,7 @@ export class YangParser {
// import all required files and set module state
if (imports) for (let ind = 0; ind < imports.length; ++ind) {
const moduleName = imports[ind].arg!;
- await this.addCapability(moduleName);
+ await this.addCapability(moduleName, undefined, module.state === ModuleState.importOnly);
const importedModule = this._modules[imports[ind].arg!];
if (importedModule && importedModule.state > ModuleState.stable) {
module.state = Math.max(module.state, ModuleState.instable);
@@ -415,7 +418,7 @@ export class YangParser {
this.extractTypeDefinitions(rootStatement, module, "");
- this.extractIdentites(rootStatement, 0, module, "");
+ this.extractIdentities(rootStatement, 0, module, "");
const groupings = this.extractGroupings(rootStatement, 0, module, "");
this._views.push(...groupings);
@@ -440,8 +443,8 @@ export class YangParser {
module.views[key] = this._views[viewIdIndex];
}
- // add only the UI View if the module is avliable
- if (module.state !== ModuleState.unabaliabe) this._views[0].elements[key] = module.elements[key];
+ // add only the UI View if the module is available
+ if (module.state === ModuleState.stable || module.state === ModuleState.instable) this._views[0].elements[key] = module.elements[key];
});
});
return module;
@@ -449,7 +452,7 @@ export class YangParser {
public postProcess() {
- // execute all post processes like resolving in propper order
+ // execute all post processes like resolving in proper order
this._unionsToResolve.forEach(cb => {
try { cb(); } catch (error) {
console.warn(error.message);
@@ -463,7 +466,7 @@ export class YangParser {
}
});
- // process all augmentations / sort by namespace changes to ensure propper order
+ // process all augmentations / sort by namespace changes to ensure proper order
Object.keys(this.modules).forEach(modKey => {
const module = this.modules[modKey];
const augmentKeysWithCounter = Object.keys(module.augments).map((key) => {
@@ -517,7 +520,7 @@ export class YangParser {
return result;
}
- const baseIdentites: Identity[] = [];
+ const baseIdentities: Identity[] = [];
Object.keys(this.modules).forEach(modKey => {
const module = this.modules[modKey];
Object.keys(module.identities).forEach(idKey => {
@@ -526,11 +529,11 @@ export class YangParser {
const base = this.resolveIdentity(identity.base, module);
base.children?.push(identity);
} else {
- baseIdentites.push(identity);
+ baseIdentities.push(identity);
}
});
});
- baseIdentites.forEach(identity => {
+ baseIdentities.forEach(identity => {
identity.values = identity.children && traverseIdentity(identity.children) || [];
});
@@ -598,7 +601,7 @@ export class YangParser {
});
}
- /** Handles Goupings like named Container */
+ /** Handles groupings like named Container */
private extractGroupings(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] {
const subViews: ViewSpecification[] = [];
const groupings = this.extractNodes(statement, "grouping");
@@ -620,7 +623,7 @@ export class YangParser {
return subViews;
}
- /** Handles Augmants also like named Container */
+ /** Handles augments also like named container */
private extractAugments(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] {
const subViews: ViewSpecification[] = [];
const augments = this.extractNodes(statement, "augment");
@@ -645,8 +648,8 @@ export class YangParser {
return subViews;
}
- /** Handles Identities */
- private extractIdentites(statement: Statement, parentId: number, module: Module, currentPath: string) {
+ /** Handles identities */
+ private extractIdentities(statement: Statement, parentId: number, module: Module, currentPath: string) {
const identities = this.extractNodes(statement, "identity");
module.identities = identities.reduce<{ [name: string]: Identity }>((acc, cur) => {
if (!cur.arg) {