Updating Packageloader

This commit is contained in:
Martin Karkowski 2021-02-12 15:54:57 +01:00
parent 9a9ddc9ac9
commit 2e10d8e3ba
12 changed files with 334 additions and 225 deletions

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2021-02-09 11:26:58
* @modify date 2021-02-12 08:18:03
* @modify date 2021-02-12 15:05:05
* @desc [description]
*/
@ -11,6 +11,7 @@ import {
ENopeDispatcherStatus,
IDispatcherInfo
} from "../types/nope/nopeDispatcher.interface";
import { INopeModule } from "../types/nope/nopeModule.interface";
import { INopeObservable } from "../types/nope/nopeObservable.interface";
import { nopeDispatcher } from "./nopeDispatcher";
@ -119,6 +120,34 @@ export class nopeDispatcherManager extends nopeDispatcher {
return Array.from(hosts);
}
/**
* Return the instances of the given Type.
*
* @param {string} type
* @return {*} {INopeModuleDescription[]}
* @memberof nopeDispatcherManager
*/
public async getInstancesOfType<I extends INopeModule>(type:string) {
const indentifier = this.availableInstances.getContent()
.filter(item => item.type == type)
.map(item => item.identifier);
const promises: Promise<I>[] = [];
for (const identifier of indentifier) {
promises.push(this.generateInstance({
identifier,
type,
params: []
}));
}
// Wait to generate all Instances.
const result = await Promise.all(promises);
return result;
}
public getHostInfos(): {
[index: string]: HostInfo
} {

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-08-25 14:52:52
* @modify date 2020-08-25 18:13:54
* @modify date 2021-02-12 14:28:46
* @desc [description]
*/
@ -10,8 +10,8 @@ declare const process: any;
declare const setImmediate: any;
const _runningInNode = (
typeof process !== 'undefined' &&
typeof process.release !== 'undefined') && ((process as any).release.name === 'node');
typeof process !== "undefined" &&
typeof process.release !== "undefined") && ((process as any).release.name === "node");
/**
* Function to call a function something direct async
@ -33,3 +33,5 @@ export const callDirect = (callback: (...args) => void, ...args) => {
};
export const RUNNINGINNODE = _runningInNode;
export const RUNNINGINWINDOWS = _runningInNode ? (require("os").type() != "Linux") : false;
export const RUNNINGINLINUX = _runningInNode ? (require("os").type() === "Linux") : false;

View File

@ -2,13 +2,14 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2018-07-01 09:10:35
* @modify date 2020-12-02 08:57:36
* @modify date 2021-02-12 15:53:57
* @desc [description]
*/
import { Container, injectable, interfaces } from "inversify";
import { flatten } from "lodash";
import "reflect-metadata";
import { promisify } from "util";
import { arraysEqual } from "../helpers/arrayMethods";
import { RUNNINGINNODE } from "../helpers/runtimeMethods";
import { getNopeLogger } from "../logger/getLogger";
@ -22,6 +23,8 @@ import {
} from "../types/nope/nopePackage.interface";
import { INopePackageLoader } from "../types/nope/nopePackageLoader.interface";
const sleep = promisify(setTimeout);
/**
* Helper Class to Build an inversify Container.
*
@ -660,6 +663,22 @@ export class NopePackageLoader implements INopePackageLoader {
// Store the Function, that the instance will be disposed on leaving.
this._disposeDefaultInstance.push(() => instance.dispose());
if (definition.options.identifier in this.packages[name].autostart){
// There are autostart Tasks in the Package for the considered Instance,
// which has been recently defined.
try {
const autostart = this.packages[name].autostart[definition.options.identifier];
for (const task of autostart ){
if (task.delay){
await sleep(task.delay);
}
await instance[task.service](task.params);
}
} catch(e) {
this._logger.error("Failed with autostart tasks for " + instance.identifier);
}
}
}
}

View File

@ -0,0 +1,140 @@
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2021-02-12 14:21:27
* @modify date 2021-02-12 15:24:33
* @desc [description]
*/
import { exec } from "child_process";
import { injectable } from "inversify";
import { wake } from "node-wol";
import { hostname, NetworkInterfaceInfo, networkInterfaces } from "os";
import { promisify } from "util";
import { exportMethod, exportProperty } from "../../../lib/decorators/moduleDecorators";
import { RUNNINGINLINUX, RUNNINGINWINDOWS } from "../../../lib/helpers/runtimeMethods";
import { getNopeLogger } from "../../../lib/logger/getLogger";
import { InjectableNopeBaseModule } from "../../../lib/module/BaseModule.injectable";
import { NopeObservable } from "../../../lib/observables/nopeObservable";
const execAsync = promisify(exec);
const wolAsync = promisify(wake);
@injectable()
export class HostManagerModule extends InjectableNopeBaseModule {
protected _logger = getNopeLogger("host-manager", "info");
@exportProperty({
mode: "publish",
schema: {
description: "An Observable containing the hostname of the controlled Host.",
type: "string"
},
topic: "controlledHost"
})
public controlledHost = new NopeObservable<string>();
@exportProperty({
mode: "publish",
schema: {
description: "An Observable containing the used Net",
type: "string"
},
topic: "networkInterfaces"
})
public networkInterfaces = new NopeObservable<{[index:string]: NetworkInterfaceInfo[]}>();
async init() {
// Define the Author.
this.author = {
forename: "Martin",
mail: "m.karkowski@zema.de",
surename: "karkowski"
};
this.description =
"The Host-Manger provides functions, to shutdow / reboot the Host.";
this.version = {
date: new Date("12.02.2020"),
version: 1
};
// Assign the Hostname to the Element.
this.controlledHost.setContent(hostname());
this.networkInterfaces.setContent(networkInterfaces());
await super.init();
const _this = this;
this._dispatcher.subscribedEvents.forcePublish();
// Singal, that the system is online
_this._logger.info("system online");
}
/**
* Function, used to kill an Process, which is running on the Host.
*
* @param {number} pid
* @memberof HostManagerModule
*/
@exportMethod({
paramsHasNoCallback: true
})
public async killProcess(pid:number){
if (RUNNINGINWINDOWS){
await execAsync("taskkill /F /PID "+pid.toString());
} else if (RUNNINGINLINUX){
throw Error("Currently not implemented");
}
}
/**
* Function, used to reboot the System
*
* @memberof HostManagerModule
*/
@exportMethod({
paramsHasNoCallback: true
})
public async reboot(){
if (RUNNINGINWINDOWS){
this._logger.warn("Rebootin host.");
await execAsync("shutdown -r");
} else if (RUNNINGINLINUX){
throw Error("Currently not implemented");
}
}
/**
* Uses a Wake on Lan Signal to the desired MAC.
*
* @param {string} mac
* @memberof HostManagerModule
*/
@exportMethod({
paramsHasNoCallback: true
})
public async wake(mac: string){
this._logger.info("Sending Wake-On-Lan to "+mac);
await wolAsync(mac);
}
/**
* Function used to shutdown a Computer.
*
* @memberof HostManagerModule
*/
@exportMethod({
paramsHasNoCallback: true
})
public async shutdown(){
if (RUNNINGINWINDOWS){
this._logger.warn("Shutdown host.");
await execAsync("shutdown -s");
} else if (RUNNINGINLINUX){
throw Error("Currently not implemented");
}
}
}

View File

@ -0,0 +1,59 @@
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2021-02-12 14:54:53
* @modify date 2021-02-12 15:22:40
* @desc [description]
*/
import { hostname } from "os";
import { IPackageDescription } from "../../../lib/types/nope/nopePackage.interface";
import { HostManagerModule } from "./hostManager.module";
const TYPES = {
HostManager: Symbol.for("HostManagerModule")
};
const identifier = hostname()+"-host-manager";
const autostart: IPackageDescription<any>["autostart"] = {};
autostart[identifier] = [
{
service: "shutdown",
params: []
}
];
export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
activationHandlers: [],
autostart: {},
defaultInstances: [
{
options: {
identifier,
params: [],
type: HostManagerModule.prototype.constructor.name.toString()
},
selector: HostManagerModule.prototype.constructor.name.toString()
}
],
nameOfPackage: "hostManagerPackage",
providedClasses: [
{
description: {
name: HostManagerModule.prototype.constructor.name.toString(),
selector: TYPES.HostManager,
type: HostManagerModule
},
settings: {
allowInstanceGeneration: true,
maxAmountOfInstance: 1
}
}
],
providedFunctions: [],
requiredPackages: [],
types: TYPES
};
export default DESCRIPTION;

5
package-lock.json generated
View File

@ -10116,6 +10116,11 @@
"xml": "0.0.12"
}
},
"node-wol": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/node-wol/-/node-wol-0.1.1.tgz",
"integrity": "sha1-m+F/6LeZxeAKaq1ciTb0NoTl6D0="
},
"noop-logger": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",

View File

@ -70,6 +70,7 @@
"node-ads": "^1.5.1",
"node-rest-client": "^3.1.0",
"node-windows": "^1.0.0-beta.5",
"node-wol": "^0.1.1",
"npm": "^7.5.2",
"npx": "^10.2.2",
"openapi-typescript-codegen": "^0.5.3",

View File

@ -1,174 +0,0 @@
import { faHatWizard } from "@fortawesome/free-solid-svg-icons";
import * as React from "react";
import { HostInfo, nopeDispatcherManager } from "../../lib/dispatcher/nopeDispatcherManager";
import { ComputingNodeOverviewComponent } from "../../resources/admin-shell/computingNodeOverview";
import { HostStatusComponent } from "../../resources/admin-shell/host/HostStatus";
import { InstanceOverviewComponent } from "../../resources/admin-shell/instanceOverview";
import DynLayout, {
IDynamicLayoutProps
} from "../../resources/ui/layout/dynamicLayout";
export interface DynLayoutProps {dispatcher: nopeDispatcherManager}
export interface DynLayoutState {computingNodes: HostInfo[]}
class TestDynLayoutComponent extends React.Component<
DynLayoutProps,
DynLayoutState
> {
componentDidMount(): void {
let nDispatchers = this.props.dispatcher.externalDispatchers.getContent().length;
const computingNodes = this.props.dispatcher.getHostInfos();
const computingNodesAsList = Object.getOwnPropertyNames(computingNodes).map(key => computingNodes[key]);
this.props.dispatcher.externalDispatchers.subscribe((dispatchers) => {
if (nDispatchers != dispatchers.length){
nDispatchers = dispatchers.length;
const computingNodes = this.props.dispatcher.getHostInfos();
const computingNodesAsList = Object.getOwnPropertyNames(computingNodes).map(key => computingNodes[key]);
this.setState({
computingNodes: computingNodesAsList
});
}
});
this.setState({
computingNodes: computingNodesAsList
});
}
constructor(props) {
super(props);
this.state = {
computingNodes: []
};
}
render(): JSX.Element {
const cols = 15;
const settings: IDynamicLayoutProps<any> = {
components: [
{
visible: true,
component(props) {
return (
<>
<h5>Nodes</h5>
<hr/>
<ComputingNodeOverviewComponent
renderDetails={false}
dispatcher={props.dispatcher}
></ComputingNodeOverviewComponent>
</>
);
},
gridSettings: {
h: 2,
w: 10,
x: 0,
y: 0,
},
id: "NopeOverview",
label: "NopeOverview",
props: {
dispatcher: this.props.dispatcher
},
hideable: true,
bg: "light"
},
{
visible: true,
component(props) {
return (
<>
<h5>Instances</h5>
<hr/>
<InstanceOverviewComponent
dispatcher={props.dispatcher}
renderDetails={false}
></InstanceOverviewComponent>
</>
);
},
gridSettings: {
h: 2,
w: 5,
x: 10,
y: 0
},
id: "InstanceOverview",
label: "InstanceOverview",
props: {
dispatcher: this.props.dispatcher
},
hideable: true,
bg: "light"
}
],
layoutSettings: {
width: process.browser ? window.innerWidth : 1920,
autoSize: false,
preventCollision: false,
cols,
compactType: "horizontal"
},
toolbar: {
items: [
{
type: "link",
label: "docs",
ref: "/docs",
icon: faHatWizard
}
]
},
generateData() {
return {
content: "hello"
};
}
};
let lineCounter = 0;
let rowCounter = 0;
for (const node of this.state.computingNodes){
settings.components.push({
component(props: {node: HostInfo, dispatcher: nopeDispatcherManager}){
return (<HostStatusComponent
host={props.node.name}
renderDetails={true}
dispatcher={props.dispatcher}
ids={{}}
></HostStatusComponent>);
},
gridSettings: {
isResizable: true,
x: rowCounter*5,
y: 2+ lineCounter*3,
h: 3,
w: 5
},
id: node.name,
label: `${node.name}-Details`,
props: {node, dispatcher: this.props.dispatcher},
visible: true,
hideable: true,
preventRenderingCard: true
});
rowCounter = rowCounter + 1;
if (rowCounter === 3){
lineCounter = lineCounter +1;
rowCounter = 0;
}
}
return React.createElement(DynLayout, settings);
}
}
export default TestDynLayoutComponent;

View File

@ -6,7 +6,8 @@
* @desc [description]
*/
import { faHashtag } from "@fortawesome/free-solid-svg-icons";
import { faHashtag, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
import {
Alert,
@ -114,17 +115,16 @@ class OverviewComponent extends React.Component<
<Button
variant="light"
style={{ width: "100%" }}
onClick={() => this._refreshState()}
onClick={() => this.props.dispatcher.emitBonjour()}
>
Manual Refresh
</Button>
</Jumbotron>
<Row>
<Col>
<Jumbotron>
<h1>
<code>NoPE</code>-Nodes
<FontAwesomeIcon icon={faInfoCircle} onClick={_ => this.setState({renderNodeDetails: !this.state.renderNodeDetails})} />{"\t"}<code>NoPE</code>-Nodes
</h1>
<p>
The following <code>NoPE</code>-Nodes has been found:
@ -149,7 +149,7 @@ class OverviewComponent extends React.Component<
<Col>
<Jumbotron>
<h1>
<code>NoPE</code>-Instances
<FontAwesomeIcon icon={faInfoCircle} onClick={_ => this.setState({renderInstanceDetails: !this.state.renderInstanceDetails})} />{"\t"}<code>NoPE</code>-Instances
</h1>
<p>
The following <code>Instances</code>-Nodes has been found:

View File

@ -73,8 +73,10 @@ export class ComputingNodeOverviewComponent extends React.Component<
const _this = this;
this._observer.push(
this.props.dispatcher.externalDispatchers.subscribe(() => {
this.props.dispatcher.externalDispatchers.subscribe((dispatchers) => {
if (_this.state.computingNodes.length != dispatchers.length || !_this.props.renderDetails) {
_this._refresh();
}
})
);
@ -124,8 +126,10 @@ export class ComputingNodeOverviewComponent extends React.Component<
return (
<Col key={`${idx_01}_${idx_02}`} sm={sm}>
<HostStatusComponent
status={item}
renderDispatcherDetails={true}
host={item.name}
dispatcher={this.props.dispatcher}
renderDetails={true}
ids={{}}
></HostStatusComponent>
</Col>
);

View File

@ -77,6 +77,11 @@ export class InstanceDetailsComponent extends React.Component<
}
render(): JSX.Element {
const renderMethods =
Object.getOwnPropertyNames(this.props.description.functions).length > 0;
const renderProperties =
Object.getOwnPropertyNames(this.props.description.properties).length > 0;
return (
<Card border={this.state.variant}>
<Card.Body>
@ -92,7 +97,9 @@ export class InstanceDetailsComponent extends React.Component<
<tbody>
<tr>
<td>Class</td>
<td><code>{this.props.description.type}</code></td>
<td>
<code>{this.props.description.type}</code>
</td>
</tr>
<tr>
<td>Redundancy</td>
@ -116,6 +123,7 @@ export class InstanceDetailsComponent extends React.Component<
)}
{this.state.renderDetails ? (
<>
{renderProperties ? (
<tr>
<td>Properties</td>
<td>
@ -131,6 +139,10 @@ export class InstanceDetailsComponent extends React.Component<
})}
</td>
</tr>
) : (
<></>
)}
{renderMethods ? (
<tr>
<td>Methods</td>
<td>
@ -146,6 +158,9 @@ export class InstanceDetailsComponent extends React.Component<
})}
</td>
</tr>
) : (
<></>
)}
</>
) : (
""

View File

@ -39,6 +39,7 @@ export class InstanceOverviewComponent extends React.Component<
const computingNodes = this.props.dispatcher.getHostInfos();
const computingNodesAsList = Object.getOwnPropertyNames(computingNodes).map(key => computingNodes[key]);
const allInstances = this.props.dispatcher.availableInstances.getContent();
console.log(allInstances.length);
const mappingIndexToName = new Map<string,number>();
const instances: InstanceDetailsState[] = allInstances.map((item,idx) => {
@ -89,8 +90,16 @@ export class InstanceOverviewComponent extends React.Component<
// Subscribe to the Instances.
const _this = this;
this._observer.push(
this.props.dispatcher.availableInstances.subscribe(() => {
console.log("HERE");
_this._refresh();
})
);
this._observer.push(
this.props.dispatcher.externalDispatchers.subscribe(() => {
console.log("HERE");
_this._refresh();
})
);