├── .gitignore
├── backend
├── config.ini
├── error.py
├── inventory.py
├── socketio.py
├── __init__.py
├── devices.py
└── data.py
├── frontend
├── plugins
│ ├── plugins.component.scss
│ ├── plugins.component.html
│ └── plugins.component.ts
├── monitoring
│ ├── monitoring.component.scss
│ ├── monitoring.component.ts
│ └── monitoring.component.html
├── common
│ ├── loading
│ │ ├── loading.component.html
│ │ ├── loading.component.ts
│ │ └── loading.component.scss
│ └── pipes.ts
├── inventory
│ ├── inventory.component.html
│ ├── schema.ts
│ ├── device.ts
│ ├── devices.service.ts
│ ├── schemas.component.html
│ ├── inventory.component.ts
│ ├── inventory.component.scss
│ ├── schemas.component.ts
│ └── devices.component.html
├── package.json
├── netopeer.component.html
├── dashboard.component.ts
├── config
│ ├── config.component.scss
│ ├── session.ts
│ ├── tree-create.html
│ ├── tree-edit.html
│ ├── tree.component.html
│ ├── tree.component.scss
│ ├── config.component.html
│ ├── tree-indent.html
│ └── tree-node.html
├── netopeer.scss
├── dashboard.component.html
├── yang
│ ├── yang.feature.html
│ ├── yang.typedef.html
│ ├── yang.restriction.html
│ ├── yang.identity.html
│ ├── yang.component.scss
│ ├── yang.component.html
│ └── yang.type.html
├── netopeer.component.ts
├── _netopeer-common.scss
└── netopeer.module.ts
├── frontend-assets
├── logo.png
└── icons
│ ├── show_children.svg
│ ├── tree_empty.svg
│ ├── show_children_active.svg
│ ├── add.svg
│ ├── add_active.svg
│ ├── close.svg
│ ├── close_active.svg
│ ├── show_all.svg
│ ├── collapse.svg
│ ├── collapse_active.svg
│ ├── show_all_active.svg
│ ├── tree_root.svg
│ ├── tree_last_branch.svg
│ ├── tree_cont.svg
│ ├── leaf.svg
│ ├── menu.svg
│ ├── menu_active.svg
│ ├── tree_branch.svg
│ ├── info.svg
│ ├── info_active.svg
│ ├── key.svg
│ ├── container.svg
│ ├── leaflist.svg
│ ├── show.svg
│ ├── show_active.svg
│ ├── edit.svg
│ ├── edit_active.svg
│ ├── back.svg
│ └── module.svg
├── vagrant
├── Ubuntu-release
│ ├── netopeer-config.ini
│ ├── run.sh
│ ├── setvenv.sh
│ ├── ncgui.service
│ ├── ncgui.conf
│ ├── lgui-config.ini
│ └── Vagrantfile
├── README.md
├── OpenSUSE
│ └── Vagrantfile
└── Ubuntu
│ └── Vagrantfile
├── docs
└── screenshots
│ ├── yang.png
│ ├── devices.png
│ └── configuration.png
├── app.config.json
├── config.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 |
--------------------------------------------------------------------------------
/backend/config.ini:
--------------------------------------------------------------------------------
1 | [netopeer]
2 | usersdata_path=./
--------------------------------------------------------------------------------
/frontend/plugins/plugins.component.scss:
--------------------------------------------------------------------------------
1 | @import '../netopeer-common';
--------------------------------------------------------------------------------
/frontend/monitoring/monitoring.component.scss:
--------------------------------------------------------------------------------
1 | @import '../netopeer-common';
--------------------------------------------------------------------------------
/frontend-assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CESNET/Netopeer2GUI/HEAD/frontend-assets/logo.png
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/netopeer-config.ini:
--------------------------------------------------------------------------------
1 | [netopeer]
2 | usersdata_path=/var/www/html/ncgui/data
3 |
--------------------------------------------------------------------------------
/docs/screenshots/yang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CESNET/Netopeer2GUI/HEAD/docs/screenshots/yang.png
--------------------------------------------------------------------------------
/docs/screenshots/devices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CESNET/Netopeer2GUI/HEAD/docs/screenshots/devices.png
--------------------------------------------------------------------------------
/docs/screenshots/configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CESNET/Netopeer2GUI/HEAD/docs/screenshots/configuration.png
--------------------------------------------------------------------------------
/backend/error.py:
--------------------------------------------------------------------------------
1 | #!/bin/python3
2 |
3 | from liberouterapi.error import ApiException
4 |
5 | class NetopeerException(ApiException):
6 | status_code = 500
7 |
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | source /var/www/html/ncgui/backend/venv/bin/activate
3 | python3 /var/www/html/ncgui/backend/ &
4 | deactivate
5 |
6 |
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/setvenv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | virtualenv venv --system-site-packages -p python3
3 | source venv/bin/activate
4 | pip3 install --upgrade pip
5 | pip3 install -r requirements.txt
6 | deactivate
7 |
8 |
--------------------------------------------------------------------------------
/frontend/common/loading/loading.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/frontend/inventory/inventory.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{ component | titlecase }}
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/ncgui.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Netopeer2GUI backend
3 |
4 | [Service]
5 | Type=oneshot
6 | ExecStart=/var/www/html/ncgui/backend/run.sh
7 | RemainAfterExit=true
8 | StandardOutput=syslog
9 | StandardError=syslog
10 |
11 | [Install]
12 | WantedBy=multi-user.target
13 |
--------------------------------------------------------------------------------
/frontend/inventory/schema.ts:
--------------------------------------------------------------------------------
1 | export class Schema {
2 | constructor (
3 | public key: string,
4 | public name: string = '',
5 | public revision: string = '',
6 | public type: string = '',
7 | public path: string = '',
8 | public data: any = null,
9 | public sections: string[] = []
10 | ) {}
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/monitoring/monitoring.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector : 'netopeer-config',
5 | templateUrl : './monitoring.component.html',
6 | styleUrls : ['./monitoring.component.scss']
7 | })
8 |
9 | export class MonitoringComponent {
10 | title = 'Monitoring';
11 | }
12 |
--------------------------------------------------------------------------------
/app.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "logo" : "assets/netopeer/logo.png",
3 | "name" : "Netopeer",
4 | "colorTheme" : {
5 | "colorMain" : "#354b68",
6 | "colorHighlight" : "#3d5676",
7 | "colorSelected" : "#3d5676",
8 | "colorSelected2" : "#6888b1"
9 | },
10 | "api" : {
11 | "url" : "/libapi",
12 | "host" : null,
13 | "port" : null,
14 | "proto" : null
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "npm" : "frontend/package.json"
4 | },
5 | "module": {
6 | "name" : "netopeer",
7 | "file" : "netopeer.module.ts",
8 | "class" : "NetopeerModule",
9 | "hooks" : "NetopeerModuleHooks",
10 | "frontend" : "frontend",
11 | "backend" : "backend",
12 | "assets" : "frontend-assets"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/monitoring/monitoring.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Monitoring (TBD)
4 |
Work with NETCONF notifications from the connected devices.
5 |
6 | subscribing for notifications
7 | setting up time windows, filters, search in the received data
8 | background monitoring (receiving notifications) with alerts sent via email
9 |
10 |
11 |
--------------------------------------------------------------------------------
/frontend/common/loading/loading.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, Input} from '@angular/core';
2 |
3 | @Component({
4 | selector : 'netopeer-loading',
5 | templateUrl : './loading.component.html',
6 | styleUrls : ['./loading.component.scss'],
7 | encapsulation: ViewEncapsulation.None
8 | })
9 |
10 | export class LoadingComponent {
11 | @Input() spinner = false;
12 | @Input() diameter = 50;
13 | @Input() strokeWidth = 7;
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/plugins/plugins.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Plugins (TBD)
4 |
Framework for schema(s) specific applications - simplified/more user friendly configuration approach than the generic configuration tree in the configuration tab, for example:
5 |
6 | visual network connections configuration
7 | connected devices time synchronisation checking
8 | ...
9 |
10 | {{text}}
11 |
--------------------------------------------------------------------------------
/frontend/inventory/device.ts:
--------------------------------------------------------------------------------
1 | export class Device {
2 | constructor (
3 | public id: number,
4 | public name:string = '',
5 | public hostname: string = '',
6 | public port: number = 830,
7 | public autoconnect: boolean = false,
8 | public username: string = '',
9 | public password: string = '',
10 | public fingerprint: string = '',
11 | ) {}
12 | }
13 | /*
14 | export class Device {
15 | id: number;
16 | hostname: string;
17 | port: number = 830;
18 | username: string;
19 | password: string;
20 | }
21 | */
22 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Netopeer2GUI",
3 | "version": "0.1.0",
4 | "description": "NETCONF management center",
5 | "main": "index.html",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/CESNET/Netopeer2GUI.git"
9 | },
10 | "author": "",
11 | "license": "BSD",
12 | "bugs": {
13 | "url": "https://github.com/CESNET/Netopeer2GUI/issues"
14 | },
15 | "homepage": "https://github.com/CESNET/Netopeer2GUI",
16 | "dependencies" : {
17 | "@angular/material": "^6.0.0",
18 | "@angular/cdk": "^6.0.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/plugins/plugins.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector : 'netopeer-plugins',
5 | templateUrl : './plugins.component.html',
6 | styleUrls : ['./plugins.component.scss']
7 | })
8 |
9 | export class PluginsComponent implements OnInit {
10 | title = 'Plugins';
11 | text = "test... ignore";
12 |
13 | sleep(ms) {
14 | return new Promise(resolve => setTimeout(resolve, ms));
15 | }
16 |
17 | async ngOnInit() {
18 | await this.sleep(2000);
19 | this.text = "still testing... still ignore";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/frontend/netopeer.component.html:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/backend/inventory.py:
--------------------------------------------------------------------------------
1 | """
2 | Manipulation with the YANG schemas.
3 | File: schemas.py
4 | Author: Radek Krejci
5 | """
6 |
7 | import os
8 | import errno
9 |
10 | from liberouterapi import config
11 |
12 | from .error import NetopeerException
13 |
14 | INVENTORY = config['netopeer'].get('usersdata_path', './')
15 |
16 | def inventory_check(path):
17 | try:
18 | os.makedirs(path, mode=0o750)
19 | except OSError as e:
20 | if e.errno == errno.EEXIST and os.path.isdir(path):
21 | pass
22 | elif e.errno == errno.EEXIST:
23 | raise NetopeerException('User\'s inventory (' + path + ') already exists and it\'s not a directory.')
24 | else:
25 | raise NetopeerException('Unable to use inventory path ' + path +' (' + str(e) + ').')
26 |
27 |
--------------------------------------------------------------------------------
/backend/socketio.py:
--------------------------------------------------------------------------------
1 | """
2 | Socket IO helper functions
3 | File: socketio.py
4 | Author: Radek Krejci
5 | """
6 |
7 | from eventlet import event
8 |
9 | from liberouterapi import socketio
10 |
11 | sio_data = {}
12 |
13 |
14 | def sio_send(data):
15 | try:
16 | e = sio_data[data['id']]
17 | e.send(data)
18 | except KeyError:
19 | pass
20 |
21 |
22 | def sio_emit(name, params):
23 | socketio.emit(name, params, callback = sio_send)
24 |
25 |
26 | def sio_wait(id):
27 | e = sio_data[id] = event.Event()
28 | return e.wait()
29 |
30 |
31 | def sio_clean(id):
32 | sio_data.pop(id, None)
33 |
34 |
35 | @socketio.on('device_auth_password')
36 | @socketio.on('hostcheck_result')
37 | @socketio.on('getschema_result')
38 | def process_answer(data):
39 | sio_send(data)
40 |
--------------------------------------------------------------------------------
/frontend/common/loading/loading.component.scss:
--------------------------------------------------------------------------------
1 | @import '../../netopeer-common';
2 |
3 | .mat-spinner circle {
4 | stroke:$colorMain;
5 | }
6 |
7 | .mat-progress-bar-background {
8 | background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20version%3D%271.1%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20xmlns%3Axlink%3D%27http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%27%20x%3D%270px%27%20y%3D%270px%27%20enable-background%3D%27new%200%200%205%202%27%20xml%3Aspace%3D%27preserve%27%20viewBox%3D%270%200%205%202%27%20preserveAspectRatio%3D%27none%20slice%27%3E%3Ccircle%20cx%3D%271%27%20cy%3D%271%27%20r%3D%271%27%20fill%3D%27%23B2EBF2%27%2F%3E%3C%2Fsvg%3E")
9 | }
10 | .mat-progress-bar-buffer{
11 | background-color:$lightGrey;
12 | }
13 | .mat-progress-bar-fill::after{
14 | background-color:$colorMain;
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/dashboard.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {Router} from '@angular/router';
3 |
4 | import {SessionsService} from './config/sessions.service';
5 |
6 | @Component({
7 | selector : 'netopeer-dashboard',
8 | templateUrl : './dashboard.component.html',
9 | styleUrls : ['./netopeer.scss', 'inventory/inventory.component.scss']
10 | })
11 |
12 | export class DashboardComponent implements OnInit {
13 |
14 | constructor(public sessionsService: SessionsService,
15 | private router: Router) {}
16 |
17 | gotoConfig(session) {
18 | this.sessionsService.changeActiveSession(session.key);
19 | this.router.navigateByUrl('/netopeer/config');
20 | }
21 |
22 | ngOnInit(): void {
23 | this.sessionsService.checkSessions();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/ncgui.conf:
--------------------------------------------------------------------------------
1 |
2 | ServerAdmin webmaster@localhost
3 | DocumentRoot /var/www/html/ncgui
4 |
5 | ErrorLog ${APACHE_LOG_DIR}/error.log
6 | CustomLog ${APACHE_LOG_DIR}/access.log combined
7 |
8 | RewriteEngine on
9 | RewriteRule ^/libapi(.*) http://[::1]:5555$1 [P]
10 |
11 | RewriteCond %{QUERY_STRING} transport=polling [NC]
12 | RewriteRule /(.*) http://[::1]:5555/$1 [P]
13 |
14 | RewriteCond %{HTTP:Upgrade} websocket [NC]
15 | RewriteRule /(.*) ws://[::1]:5555/$1 [P]
16 |
17 |
18 | AllowOverride All
19 | Options -Indexes
20 |
21 | RewriteCond %{REQUEST_FILENAME} -f [OR]
22 | RewriteCond %{REQUEST_FILENAME} -l [OR]
23 | RewriteCond %{REQUEST_FILENAME} -d
24 | RewriteRule ^.*$ - [L]
25 | RewriteRule ^.* /index.html [L]
26 |
27 |
28 |
--------------------------------------------------------------------------------
/frontend/config/config.component.scss:
--------------------------------------------------------------------------------
1 | @import '../netopeer-common';
2 | @import '../inventory/inventory.component';
3 | @import './tree.component';
4 |
5 | #config-data {
6 | cursor: default;
7 | width: 100%;
8 | }
9 |
10 | .item_action_expand,
11 | .item_action_collapse {
12 | height: 1em;
13 | }
14 |
15 | .item_action_collapse:hover {
16 | color: $red;
17 | }
18 |
19 | .item_action_expand:hover {
20 | color: $green;
21 | }
22 |
23 | .modifications-status {
24 | margin-bottom: 2em;
25 | margin-right: 2em;
26 | padding: 0.5em 1em;
27 |
28 | >div {
29 | position: fixed;
30 | bottom: 2em;
31 | right: 2em;
32 | background-color: $colorChanged;
33 | border: 2px solid $colorChangedBorder;
34 | padding: 0 1em;
35 | div {
36 | padding: 0.5em 0;
37 | }
38 | }
39 | }
40 |
41 | .loading {
42 | text-align: center;
43 | margin: auto;
44 | width: 10em;
45 | div {
46 | margin: auto;
47 | width: 50px;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/frontend/netopeer.scss:
--------------------------------------------------------------------------------
1 | @import 'netopeer-common';
2 |
3 | #netopeer-header {
4 | position: fixed;
5 | width: 100%;
6 | margin: -0.5em -1em 0em -1em;
7 | padding-top: 1em;
8 | background-color: $colorMain;
9 | color: $colorTextInverse;
10 | display: block;
11 | }
12 |
13 | #netopeer-header h1 {
14 | margin-left: 1em;
15 | color: $colorTextInverse;
16 | }
17 |
18 | #netopeer-component {
19 | margin: 0em -1em 0em -1em;
20 | }
21 |
22 | #mainnav {
23 | with: 100%;
24 | padding-left: 1em;
25 | }
26 |
27 | #mainnav a:visited,
28 | #mainnav a:link {
29 | color: inherit;
30 | }
31 |
32 | #mainnav a:hover {
33 | border-top-color: $colorHighlight;
34 | background-color: $colorHighlight;
35 | }
36 |
37 | #mainnav a.active {
38 | border-top-color: $colorSelected2;
39 | background-color: $colorSelected;
40 | }
41 | #mainnav a.active:hover {
42 | cursor: default;
43 | }
44 |
45 | #mainnav a {
46 | text-decoration: none;
47 | display: inline-block;
48 | padding: 0.5em 1em 0.5em 0.5em;
49 | color: $colorTextInverse;
50 | border-top: 0.2em solid $colorMain;
51 | }
52 |
--------------------------------------------------------------------------------
/frontend/dashboard.component.html:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/frontend/config/session.ts:
--------------------------------------------------------------------------------
1 | import { Device } from '../inventory/device';
2 |
3 | import { SessionsService } from './sessions.service';
4 |
5 | export enum NodeType {
6 | container = 1,
7 | leaf = 4,
8 | leaflist = 8,
9 | list = 16
10 | }
11 |
12 | export class Session {
13 | constructor (
14 | public key: string,
15 | public device: Device,
16 | public loading = false,
17 | public data: Node = null,
18 | public treeFilters = [],
19 | public modifications = null,
20 | public cpblts: string = "",
21 | public dataPresence: string = 'none',
22 | public statusVisibility: boolean = true,
23 | public cpbltsVisibility: boolean = false,
24 | ) {}
25 | }
26 |
27 | export class NodeSchema {
28 | /*
29 | * type: NodeType;
30 | * path: string;
31 | */
32 | }
33 |
34 | export class Node {
35 | /*
36 | * path: string;
37 | * info: NodeSchema;
38 | *
39 | * === container ===
40 | * children: Node[]
41 | * newChildren: Node[]
42 | *
43 | * === leaf ===
44 | * value: string;
45 | *
46 | * === leaf-list ===
47 | * value: string;
48 | *
49 | * === list ===
50 | * children: Node[]
51 | * newChildren: Node[]
52 | */
53 | }
54 |
--------------------------------------------------------------------------------
/frontend/yang/yang.feature.html:
--------------------------------------------------------------------------------
1 |
2 |
Feature {{name}}
3 |
8 |
9 |
10 |
11 | status {{data.status.value}}
12 |
13 |
14 | description
15 |
16 |
17 | reference
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/frontend/config/tree-create.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 | {{modsService.schemaName(node['info'], schema)}}
11 |
12 |
13 |
14 |
15 |
16 |
17 | x There is no element to create at {{node['path']}}.
18 |
19 |
20 |
--------------------------------------------------------------------------------
/frontend/inventory/devices.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { Observable } from 'rxjs/Observable';
4 | import { catchError } from 'rxjs/operators';
5 |
6 | import { Device } from './device';
7 |
8 | @Injectable()
9 | export class DevicesService {
10 | constructor(private http: HttpClient) {}
11 |
12 | getDevices(): Observable {
13 | return this.http.get('/netopeer/inventory/devices/list')
14 | .pipe(
15 | catchError(err => Observable.throw(err))
16 | );
17 | }
18 |
19 | addDevice(device: Device) {
20 | // let options = new HttpOptions({ body: JSON.stringify(device) });
21 | return this.http.post('/netopeer/inventory/devices', device)
22 | .pipe(
23 | catchError(err => Observable.throw(err))
24 | );
25 | }
26 |
27 | rmDevice(device_id: number) {
28 | // We need to use generic HTTP request, because HttpClient does not support body in DELETE requests.
29 | return this.http.request('DELETE', '/netopeer/inventory/devices', { body: JSON.stringify({'id':device_id}) })
30 | .pipe(
31 | catchError(err => Observable.throw(err))
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/yang/yang.typedef.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Typedef {{name}}
4 |
5 |
6 |
7 |
8 |
9 | units {{data.units.name}}
10 |
11 |
12 | default {{data.default.value}}
13 |
14 |
15 | status {{data.status.value}}
16 |
17 |
18 | description
19 |
20 |
21 | reference
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/frontend/yang/yang.restriction.html:
--------------------------------------------------------------------------------
1 |
2 | {{name}}
3 |
4 |
5 |
6 | {{name}}
7 | {{data.value}}
8 |
9 |
10 |
11 | modifier
12 | {{data.modifier.value}}
13 |
14 |
15 | error-message
16 | {{data['error-message'].value}}
17 |
18 |
19 | error-app-tag
20 | {{data['error-app-tag'].value}}
21 |
22 |
23 |
description
24 |
{{data.description.text}}
25 |
26 |
27 |
reference
28 |
{{data.reference.text}}
29 |
30 |
--------------------------------------------------------------------------------
/frontend/yang/yang.identity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Identity {{name}}
4 |
9 |
10 |
15 |
16 |
17 |
18 | status {{data.status.value}}
19 |
20 |
21 | description
22 |
23 |
24 | reference
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/frontend/inventory/schemas.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Refresh
5 |
Add
6 |
7 |
8 |
9 |
10 |
11 | x Parsing {{uploadSchema.value.replace("C:\\fakepath\\","")}} failed.
12 | x Schema {{uploadSchema.value.replace("C:\\fakepath\\","")}} successfully added.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
31 |
32 | {{schema.name}}
33 | {{schema.revision}}
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/frontend/common/pipes.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
3 |
4 | @Pipe({name: 'noPrefix'})
5 | export class NoPrefixPipe implements PipeTransform {
6 | transform(value: string): string {
7 | return value.slice(value.indexOf(':') + 1);
8 | }
9 | }
10 |
11 | @Pipe({name: 'prefixOnly'})
12 | export class PrefixOnlyPipe implements PipeTransform {
13 | transform(value: string): string {
14 | return value.slice(0, value.indexOf(':'));
15 | }
16 | }
17 |
18 | @Pipe({name: 'patternHighlight'})
19 | export class PatternHighlightPipe implements PipeTransform {
20 | constructor(private _sanitizer:DomSanitizer) {}
21 |
22 | transform(value: string): SafeHtml {
23 | let result = '';
24 | for(let i = 0; i < value.length; i++) {
25 | if (value[i] == '(' || value[i] == '[' || value[i] == '{') {
26 | result = result.concat(`` + value[i] + ` `);
27 | } else if (value[i] == ')' || value[i] == ']' || value[i] == '}') {
28 | let data = value[i];
29 | if (i + 1 < value.length && (value[i+1] == '?' || value[i+1] == '+' || value[i+1] == '*')) {
30 | i;
31 | data = value.slice(i, i + 2);
32 | i++;
33 | }
34 | result = result.concat(`` + data + ` `);
35 | } else {
36 | result = result.concat(value[i]);
37 | }
38 | }
39 | return this._sanitizer.bypassSecurityTrustHtml(result);
40 | }
41 | }
--------------------------------------------------------------------------------
/frontend/yang/yang.component.scss:
--------------------------------------------------------------------------------
1 | @import '../netopeer-common';
2 | @import '../inventory/inventory.component';
3 |
4 | $colorLineHover: #e1e1e1;
5 | $colorLineSelected: #999999;
6 |
7 | .nav-button {
8 | height: 2em;
9 | cursor: pointer;
10 | margin: 0.3em;
11 | }
12 |
13 | .loading {
14 | text-align: center;
15 | margin: auto;
16 | width: 10em;
17 | div {
18 | margin: auto;
19 | width: 50px;
20 | }
21 | }
22 |
23 | .yang-infobox {
24 | display: block;
25 | cursor: default;
26 | }
27 |
28 | .yang-info-section-label,
29 | .yang-info-subsection-label,
30 | .yang-info-label {
31 | color: black;
32 | font-weight: 100;
33 | text-transform: uppercase;
34 | }
35 |
36 | .yang-info-section {
37 | display: flex;
38 | flex-direction: column;
39 | flex-wrap: wrap;
40 | width: 100%;
41 | padding-left: 2em;
42 | }
43 |
44 | .yang-info-subsection {
45 | .yang-info {
46 | padding-left: 2em;
47 | }
48 | }
49 |
50 | .yang-info,
51 | .yang-info-label,
52 | .yang-info-value {
53 | border: none;
54 | a {
55 | font-weight: normal;
56 | }
57 | }
58 |
59 | .yang-info {
60 | display: flex;
61 | padding: 5px;
62 | }
63 | .yang-info:hover {
64 | background-color: $colorLineHover;
65 | }
66 |
67 | .yang-info:nth-of-type(even), .yang-info-subsection:nth-of-type(even) {
68 | background: lighten($colorLineHover, 5%);
69 | }
70 |
71 | .yang-info-value {
72 | display: inline-block;
73 | }
74 |
75 | .yang-info-section-label,
76 | .yang-info-subsection-label,
77 | .yang-info-label {
78 | min-width: 10em;
79 | overflow: hidden;
80 | text-overflow: ellipsis;
81 | display: inline-block;
82 | }
83 | .yang-revision-label {
84 | font-style: italic;
85 | }
86 |
87 | .yang-subsection-container {
88 | margin-left: 2em;
89 | }
90 |
91 | .pattern {
92 | .selectedGroup {
93 | background-color: $colorLineSelected;
94 | }
95 | .bracket {
96 | }
97 | }
98 |
99 |
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/lgui-config.ini:
--------------------------------------------------------------------------------
1 | [api]
2 | ; Enable debugging mode
3 | ; This sets logging to debug (most verbose) and enables Flask's debugging mode
4 | ; DEFAULT: false
5 | debug = true
6 |
7 | ; Specify host on which to run
8 | ; Use only in combination with running API directly as a package. Otherwise this setting is ignored.
9 | ; DEFAULT: localhost
10 | ;host =
11 |
12 | ; Specify port on which to run
13 | ; Use only in combination with running API directly as a package. Otherwise this setting is ignored.
14 | ; DEFAULT: 5555
15 | port = 5555
16 | threaded = true
17 |
18 | version = 1.0
19 | modules = /modules
20 | ssl = false
21 |
22 | ; Authorization Providers
23 | ; By default the only authorization provider is the database (defined below)
24 | ; Liberouter GUI can utilize other providers using Linux PAM (pluggable authentication modules). If
25 | ; you desire such feature just uncomment following line
26 | ; DEFAULT: false
27 | ;pam = true
28 |
29 | ; Enable Cross-Origin Resource Sharing
30 | ; WARNING: This feature is not recommended due to its security concerns. Try to use proxying in the
31 | ; first place.
32 | ; DEFAULT: false
33 | ;cors = true
34 |
35 | ; Session Timeout (seconds)
36 | ; States how long a session will be valid in seconds
37 | ; DEFAULT: 900
38 | ;session_timeout = 900
39 |
40 | ; Max User Session
41 | ; Limit number of sessions created by a single unique user
42 | ; DEFAULT: 10
43 | ;session_max_per_user = 10
44 |
45 | [database]
46 | ; possible values: sqlite, mysql, mongodb
47 | ; sqlite: file must be specified, the server and port are ignored
48 | ; mysql: server, port and database must be specified, user and password
49 | ; are for authentication to the db
50 | ; mongodb: server, port and database must be set
51 | provider = sqlite
52 | users = users
53 | configuration = configuration
54 |
55 | [mongodb]
56 | server = localhost
57 | port = 27017
58 | ;user =
59 | ;password =
60 | database = liberouter
61 |
62 | [sqlite]
63 | file = /var/www/html/ncgui/data/ncgui-users.sq3
64 |
65 | [ssl]
66 | ;key =
67 | ;certificate =
68 |
--------------------------------------------------------------------------------
/backend/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Netopeer2 GUI backend
3 | File: __init__.py
4 | Author: Radek Krejci
5 |
6 | Backend initialization via liberouter GUI.
7 | """
8 |
9 | from liberouterapi import config, modules
10 |
11 | # Get Netopeer backend config
12 | config.load(path = __path__[0] + '/config.ini')
13 |
14 | # Register a blueprint
15 | module_bp = modules.module.Module('netopeer', __name__, url_prefix = '/netopeer', no_version = True)
16 |
17 | from .schemas import *
18 | from .devices import *
19 | from .connections import *
20 |
21 | module_bp.add_url_rule('/inventory/schemas', view_func = schemas_list, methods = ['GET'])
22 | module_bp.add_url_rule('/inventory/schemas', view_func = schemas_add, methods=['POST'])
23 | module_bp.add_url_rule('/inventory/schemas', view_func = schemas_rm, methods = ['DELETE'])
24 | module_bp.add_url_rule('/inventory/schema', view_func = schema_get, methods = ['GET'])
25 | module_bp.add_url_rule('/inventory/devices/list', view_func = devices_list, methods=['GET'])
26 | module_bp.add_url_rule('/inventory/devices', view_func = devices_add, methods=['POST'])
27 | module_bp.add_url_rule('/inventory/devices', view_func = devices_rm, methods = ['DELETE'])
28 | module_bp.add_url_rule('/session', view_func = connect, methods=['POST'])
29 | module_bp.add_url_rule('/session', view_func = session_close, methods = ['DELETE'])
30 | module_bp.add_url_rule('/session/alive', view_func = session_alive, methods=['GET'])
31 | module_bp.add_url_rule('/session/capabilities', view_func = session_get_capabilities, methods=['GET'])
32 | module_bp.add_url_rule('/session/rpcGet', view_func = session_get, methods=['GET'])
33 | module_bp.add_url_rule('/session/commit', view_func = session_commit, methods = ['POST'])
34 | module_bp.add_url_rule('/session/element/checkvalue', view_func = data_checkvalue, methods = ['GET'])
35 | module_bp.add_url_rule('/session/schema', view_func = schema_info, methods = ['GET'])
36 | module_bp.add_url_rule('/session/schema/checkvalue', view_func = schema_checkvalue, methods = ['GET'])
37 | module_bp.add_url_rule('/session/schema/values', view_func = schema_values, methods = ['GET'])
38 |
--------------------------------------------------------------------------------
/frontend/config/tree-edit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
14 |
18 |
19 | {{value}}
20 |
21 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/frontend/inventory/inventory.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
3 |
4 | @Component({
5 | selector : 'netopeer-inventory',
6 | templateUrl : './inventory.component.html',
7 | styleUrls : ['./inventory.component.scss']
8 | })
9 |
10 | export class InventoryComponent {
11 | title = 'Inventory';
12 | inventoryComponents = [
13 | 'devices',
14 | 'schemas'
15 | ];
16 |
17 | constructor() { }
18 | }
19 |
20 | @Component({
21 | selector: 'ngbd-modal-content',
22 | styleUrls: ['../netopeer.scss'],
23 | template: `
26 |
27 | The device utilize YANG schema {{info.name}} in revision {{info.revision}} .
28 | Please provide this schema.
29 | Please provide submodule {{info['submod_name']}} in revision {{info['submod_revision']}} for this schema.
30 |
31 |
32 |
33 | `
36 | })
37 | export class DialogueSchema implements OnInit {
38 | @Input() info;
39 | password = '';
40 |
41 | constructor(public activeModal: NgbActiveModal) { }
42 |
43 | upload(schema: File) {
44 | let reader = new FileReader();
45 |
46 | console.log(schema);
47 | reader.onloadend = () => {
48 | //console.log(reader.result);
49 | this.activeModal.close({'filename': schema.name, 'data': reader.result});
50 | };
51 | reader.readAsText(schema);
52 | }
53 |
54 | ngOnInit(): void {
55 | document.getElementById('uploadSchema').focus();
56 | }
57 | }
--------------------------------------------------------------------------------
/frontend-assets/icons/show_children.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
65 |
--------------------------------------------------------------------------------
/frontend-assets/icons/tree_empty.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
66 |
67 |
--------------------------------------------------------------------------------
/frontend-assets/icons/show_children_active.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
65 |
--------------------------------------------------------------------------------
/frontend-assets/icons/add.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/add_active.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/close_active.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/show_all.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/collapse.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/collapse_active.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/show_all_active.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
70 |
--------------------------------------------------------------------------------
/frontend-assets/icons/tree_root.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
54 |
57 |
58 |
65 |
71 |
72 |
--------------------------------------------------------------------------------
/frontend-assets/icons/tree_last_branch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
20 |
21 |
23 | image/svg+xml
24 |
26 |
27 |
28 |
29 |
30 |
32 |
54 |
57 |
58 |
65 |
71 |
72 |
--------------------------------------------------------------------------------
/frontend-assets/icons/tree_cont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
20 |
22 |
23 |
25 | image/svg+xml
26 |
28 |
29 |
30 |
31 |
32 |
34 |
56 |
59 |
60 |
67 |
73 |
74 |
--------------------------------------------------------------------------------
/frontend-assets/icons/leaf.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
65 |
66 |
--------------------------------------------------------------------------------
/frontend/netopeer.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | import { SessionsService } from './config/sessions.service';
4 | import { DevicesService } from './inventory/devices.service';
5 | import { Device } from './inventory/device';
6 |
7 | class NComponent {
8 | route: string;
9 | name: string;
10 | }
11 |
12 | const NCOMPONENTS: NComponent[] = [
13 | { route : 'inventory', name: 'Inventory' },
14 | { route : 'config', name: 'Configuration' },
15 | { route : 'yang', name: 'YANG Explorer' },
16 | { route : 'monitoring', name: 'Monitoring' },
17 | { route : 'plugins', name: 'Plugins' }
18 | ];
19 |
20 | @Component({
21 | selector : 'netopeer',
22 | templateUrl : './netopeer.component.html',
23 | styleUrls : ['./netopeer.scss'],
24 | })
25 |
26 | export class NetopeerComponent implements OnInit {
27 | componentTitle = '';
28 | netopeerComponents = NCOMPONENTS;
29 |
30 | constructor(private sessionsService: SessionsService,
31 | private devicesService: DevicesService) { }
32 |
33 | ngOnInit() {
34 | /* autoconnect selected devices if needed */
35 | if (localStorage.getItem('netopeer-autoconnect') == 'enabled') {
36 | let ac_sessions: number[] = []; /* currently connected autoconnect devices' ids */
37 | for (let session of this.sessionsService.sessions) {
38 | if (session.device.autoconnect) {
39 | ac_sessions.push(session.device.id);
40 | }
41 | }
42 | let ac_devices: Device[] = []; /* devices with enabled autoconnect */
43 | this.devicesService.getDevices().subscribe(devices => {
44 | for (let device of devices) {
45 | if (!device['autoconnect']) {
46 | continue;
47 | }
48 | let i = ac_sessions.indexOf(device.id);
49 | if (i != -1) {
50 | ac_sessions.splice(i, 1);
51 | continue;
52 | }
53 | /* we have not connected autoconnect device */
54 | ac_devices.push(device);
55 | }
56 | for (let device of ac_devices) {
57 | this.sessionsService.connect(device).subscribe();
58 | }
59 | localStorage.setItem('netopeer-autoconnect', 'done');
60 | });
61 | }
62 | }
63 |
64 | onActivate(componentRef) {
65 | this.componentTitle = componentRef.title;
66 | }
67 | onDeactivate(componentRef) {
68 | this.componentTitle = '';
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/frontend-assets/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
74 |
75 |
--------------------------------------------------------------------------------
/frontend-assets/icons/menu_active.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
22 |
24 | image/svg+xml
25 |
27 |
28 |
29 |
30 |
31 |
33 |
55 |
58 |
59 |
64 |
69 |
74 |
75 |
--------------------------------------------------------------------------------
/vagrant/Ubuntu-release/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | $init = <