gui CHANGE show schemas in YANG explorer
very simple view in textual YANG form
diff --git a/backend/__init__.py b/backend/__init__.py
index 728ebc1..92ae9c0 100644
--- a/backend/__init__.py
+++ b/backend/__init__.py
@@ -18,9 +18,10 @@
from .devices import *
from .connections import *
-module_bp.add_url_rule('/inventory/schemas/list', view_func = schemas_list, methods=['GET'])
+module_bp.add_url_rule('/inventory/schemas', view_func = schemas_list, methods = ['GET'])
module_bp.add_url_rule('/inventory/schemas', view_func = schemas_add, methods=['POST'])
module_bp.add_url_rule('/inventory/schemas', view_func = schemas_rm, methods = ['DELETE'])
+module_bp.add_url_rule('/inventory/schema', view_func = schema_get, methods = ['GET'])
module_bp.add_url_rule('/inventory/devices/list', view_func = devices_list, methods=['GET'])
module_bp.add_url_rule('/inventory/devices', view_func = devices_add, methods=['POST'])
module_bp.add_url_rule('/inventory/devices', view_func = devices_rm, methods = ['DELETE'])
diff --git a/backend/schemas.py b/backend/schemas.py
index a3bb65d..7b12e9d 100644
--- a/backend/schemas.py
+++ b/backend/schemas.py
@@ -48,7 +48,7 @@
# initialize the list with libyang's internal modules
modules = ctx.get_module_iter()
for module in modules:
- schemas['schemas']['schema'].append({'name':module.name(),'revision':module.rev().date()})
+ schemas['schemas']['schema'].append({'key':module.name() + '@' + module.rev().date(), 'name':module.name(), 'revision':module.rev().date()})
return schemas
@@ -104,9 +104,14 @@
try:
module = __schema_parse(schemapath, format)
if module.rev_size():
- schemas['schemas']['schema'].append({'name':module.name(), 'revision':module.rev().date()})
+ schemas['schemas']['schema'].append({'key':module.name() + '@' + module.rev().date(),
+ 'name':module.name(),
+ 'revision':module.rev().date(),
+ 'file':os.path.basename(schemapath)})
else:
- schemas['schemas']['schema'].append({'name':module.name()})
+ schemas['schemas']['schema'].append({'key':module.name() + '@',
+ 'name':module.name(),
+ 'file':os.path.basename(schemapath)})
except Exception as e:
continue
@@ -127,6 +132,34 @@
return(json.dumps(schemas))
+
+@auth.required()
+def schema_get():
+ session = auth.lookup(request.headers.get('Authorization', None))
+ user = session['user']
+ req = request.args.to_dict()
+ path = os.path.join(INVENTORY, user.username)
+
+ if not 'key' in req:
+ return(json.dumps({'success': False, 'error-msg': 'Missing schema key.'}))
+ key = req['key']
+
+ schemas = __schemas_inv_load(path)
+ for i in range(len(schemas['schemas']['schema'])):
+ schema = schemas['schemas']['schema'][i]
+ if schema['key'] == key:
+ data = ""
+ if 'file' in schema:
+ with open(os.path.join(path, schema['file']), 'r') as schema_file:
+ data = schema_file.read()
+ else:
+ ctx = yang.Context()
+ data = ctx.get_module(schema['name']).print_mem(yang.LYS_OUT_YANG, 0)
+ return(json.dumps({'success': True, 'data': data}))
+
+ return(json.dumps({'success': False, 'error-msg':'Schema ' + key + ' not found.'}))
+
+
@auth.required()
def schemas_add():
if 'schema' not in request.files:
@@ -162,23 +195,18 @@
user = session['user']
path = os.path.join(INVENTORY, user.username)
- schema_rm = request.get_json()
- if not schema_rm:
+ key = request.get_json()
+ if not key:
raise NetopeerException('Invalid schema remove request.')
schemas = __schemas_inv_load(path)
for i in range(len(schemas['schemas']['schema'])):
schema = schemas['schemas']['schema'][i]
- if 'revision' in schema_rm:
- if schema['name'] != schema_rm['name'] or not 'revision' in schema or schema['revision'] != schema_rm['revision']:
- schema = None
- continue
+ if schema['key'] == key:
+ schemas['schemas']['schema'].pop(i)
+ break;
else:
- if schema['name'] != schema_rm['name'] or 'revision' in schema:
- schema = None
- continue
- schemas['schemas']['schema'].pop(i)
- break;
+ schema = None;
if not schema:
# schema not in inventory
diff --git a/frontend/_netopeer-common.scss b/frontend/_netopeer-common.scss
index 7fc0782..10ea395 100644
--- a/frontend/_netopeer-common.scss
+++ b/frontend/_netopeer-common.scss
@@ -50,4 +50,67 @@
.msg-close {
color: $colorFailureBorder;
}
+}
+
+.tab-add,
+.tab-reload,
+.tab-close {
+ cursor: pointer;
+ font-weight: bold;
+ font-family: monospace;
+ font-size: large;
+}
+.tab-action-first {
+ padding-left: 0.3em;
+}
+.tab-action-last {
+ padding-right: 0.5em;
+}
+
+.tab-icon {
+ vertical-align: middle;
+ height: 1em;
+}
+
+.tab-add {
+ filter: invert(100%);
+
+ &:hover {
+ color: $green;
+ }
+}
+
+.tab-close:hover {
+ color: $colorFailureBorder;
+}
+.tab-reload:hover {
+ color: blue;
+}
+
+#subnav {
+ position: fixed;
+ width: 100%;
+ background-color: $colorMain;
+ padding-left: 1em;
+
+ a {
+ cursor: pointer;
+ text-decoration: none;
+ display: inline-block;
+ padding: 0.2em 0 0.1em 1em;
+ color: $colorTextInverse;
+
+ &:visited,
+ &:link {
+ color: inherit;
+ }
+ &:hover,
+ &.active {
+ background-color: $colorBackground;
+ color: $colorText;
+ }
+ &active:hover {
+ cursor: default;
+ }
+ }
}
\ No newline at end of file
diff --git a/frontend/config/config.component.html b/frontend/config/config.component.html
index 3b5a6a6..9d36665 100644
--- a/frontend/config/config.component.html
+++ b/frontend/config/config.component.html
@@ -1,7 +1,6 @@
-<nav #confignav id="confignav">
+<nav #subnav id="subnav">
<a *ngFor="let session of sessionsService.sessions" [class.active]="session.key==activeSession.key"
(click)="changeActiveSession(session.key)">{{session.device.hostname}}:{{session.device.port}}
- <!--<span *ngIf="session.key==activeSession.key" class="tab-reload tab-action-first" (click)="reloadData(session.key)">o</span>-->
<img *ngIf="session.key==activeSession.key" class="tab-icon tab-reload tab-action-first" src="assets/netopeer/icons/reload.svg" alt="o" title="reload"
onmouseover="this.src='assets/netopeer/icons/reload_active.svg'"
onmouseout="this.src='assets/netopeer/icons/reload.svg'" (click)="reloadData()"/>
@@ -15,8 +14,8 @@
</a>
</nav>
-<div class="netopeer-content" [style.padding-top]="'calc(' + confignav.offsetHeight + 'px - -0.7em)'">
-<div *ngIf="sessionsService.activeSession">
+<div class="netopeer-content" [style.padding-top]="'calc(' + subnav.offsetHeight + 'px - -0.7em)'">
+<div *ngIf="activeSession">
<p class="msg-failure msg-rounded" *ngIf="err_msg"><img class="msg-close" (click)="err_msg=''" src="assets/netopeer/icons/close_active.svg" alt="x" title="close"/>{{err_msg}}</p>
<table class="items">
<tr class="item_header">
diff --git a/frontend/config/config.component.scss b/frontend/config/config.component.scss
index 3e21d41..e964cbd 100644
--- a/frontend/config/config.component.scss
+++ b/frontend/config/config.component.scss
@@ -2,69 +2,6 @@
@import '../inventory/inventory.component';
@import './tree.component';
-.tab-add,
-.tab-reload,
-.tab-close {
- cursor: pointer;
- font-weight: bold;
- font-family: monospace;
- font-size: large;
-}
-.tab-action-first {
- padding-left: 0.3em;
-}
-.tab-action-last {
- padding-right: 0.5em;
-}
-
-.tab-icon {
- vertical-align: middle;
- height: 1em;
-}
-
-.tab-add {
- filter: invert(100%);
-
- &:hover {
- color: $green;
- }
-}
-
-.tab-close:hover {
- color: $colorFailureBorder;
-}
-.tab-reload:hover {
- color: blue;
-}
-
-#confignav {
- position: fixed;
- width: 100%;
- background-color: $colorMain;
- padding-left: 1em;
-
- a {
- cursor: pointer;
- text-decoration: none;
- display: inline-block;
- padding: 0.2em 0 0.1em 1em;
- color: $colorTextInverse;
-
- &:visited,
- &:link {
- color: inherit;
- }
- &:hover,
- &.active {
- background-color: $colorBackground;
- color: $colorText;
- }
- &active:hover {
- cursor: default;
- }
- }
-}
-
#config-data {
width: 100%;
}
@@ -83,21 +20,21 @@
}
.modifications-status {
- margin-bottom: 2em;
- margin-right: 2em;
- padding: 0.5em 1em;
+ margin-bottom: 2em;
+ margin-right: 2em;
+ padding: 0.5em 1em;
- >div {
- position: fixed;
- bottom: 2em;
- right: 2em;
- background-color: $colorChanged;
- border: 2px solid $colorChangedBorder;
+ >div {
+ position: fixed;
+ bottom: 2em;
+ right: 2em;
+ background-color: $colorChanged;
+ border: 2px solid $colorChangedBorder;
padding: 0 1em;
div {
padding: 0.5em 0;
}
- }
+ }
}
.loading {
diff --git a/frontend/config/config.component.ts b/frontend/config/config.component.ts
index a41f414..ee12741 100644
--- a/frontend/config/config.component.ts
+++ b/frontend/config/config.component.ts
@@ -269,7 +269,7 @@
ngOnInit(): void {
this.sessionsService.checkSessions();
this.activeSession = this.sessionsService.getActiveSession();
- if (!this.activeSession.data) {
+ if (this.activeSession && !this.activeSession.data) {
this.treeService.rpcGet(this.activeSession, false);
}
}
diff --git a/frontend/inventory/inventory.component.ts b/frontend/inventory/inventory.component.ts
index 52e8ce0..395f1ec 100644
--- a/frontend/inventory/inventory.component.ts
+++ b/frontend/inventory/inventory.component.ts
@@ -1,5 +1,4 @@
-import { Component, OnInit } from '@angular/core';
-import { Router } from '@angular/router';
+import { Component } from '@angular/core';
@Component({
selector : 'netopeer-inventory',
@@ -7,17 +6,12 @@
styleUrls : ['./inventory.component.scss']
})
-export class InventoryComponent implements OnInit {
+export class InventoryComponent {
title = 'Inventory';
inventoryComponents = [
'devices',
'schemas'
];
- constructor(private router: Router) { }
-
- ngOnInit(): void {
- /* redirect to default inventory */
- this.router.navigateByUrl('/netopeer/inventory/devices')
- }
+ constructor() { }
}
diff --git a/frontend/inventory/schema.ts b/frontend/inventory/schema.ts
index 1a21a5a..fc0fa31 100644
--- a/frontend/inventory/schema.ts
+++ b/frontend/inventory/schema.ts
@@ -1,4 +1,5 @@
export class Schema {
- name: string;
- revision: string;
+ key: string;
+ name: string;
+ revision: string;
}
diff --git a/frontend/inventory/schemas.component.ts b/frontend/inventory/schemas.component.ts
index b7b5f2b..a1262c1 100644
--- a/frontend/inventory/schemas.component.ts
+++ b/frontend/inventory/schemas.component.ts
@@ -2,53 +2,56 @@
* Schemas Inventory
*/
import { Component, Input, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
import { Schema } from './schema';
-import { SchemasService } from './schemas.service'
+import { SchemasService } from '../yang/schemas.service'
-@Component({
- selector : 'inventorySchemas',
- templateUrl : './schemas.component.html',
- styleUrls : ['./inventory.component.scss'],
- providers: [SchemasService]
-})
+@Component( {
+ selector: 'inventorySchemas',
+ templateUrl: './schemas.component.html',
+ styleUrls: ['./inventory.component.scss']
+} )
export class InventorySchemasComponent implements OnInit {
- schemas: Schema[];
- @Input() selectedSchema: Schema;
- addingSchema = false;
- addingResult = -1;
- constructor(private schemasService: SchemasService) { }
+ schemas: Schema[];
+ @Input() selectedSchema: Schema;
+ addingSchema = false;
+ addingResult = -1;
+ constructor( private schemasService: SchemasService,
+ private router: Router ) { }
- getSchemas(): void {
- this.schemasService.getSchemas().subscribe(schemas => this.schemas = schemas);
- }
+ getSchemas(): void {
+ this.schemasService.getSchemas().then( result => this.schemas = result );
+ }
- showAddSchema() {
- this.addingSchema = !this.addingSchema;
- this.addingResult = -1;
- }
+ showAddSchema() {
+ this.addingSchema = !this.addingSchema;
+ this.addingResult = -1;
+ }
- upload(schema: File) {
- if (!schema) {
- /* do nothing */
- return;
- }
+ upload( schema: File ) {
+ if ( !schema ) {
+ /* do nothing */
+ return;
+ }
- /* upload the schema file to the server, if success the schema list is refreshed */
- this.schemasService.addSchema(schema).subscribe(
- result => {this.addingResult = result['success'] ? 1 : 0; this.getSchemas()});
- }
+ /* upload the schema file to the server, if success the schema list is refreshed */
+ this.schemasService.addSchema( schema ).subscribe(
+ result => { this.addingResult = result['success'] ? 1 : 0; this.getSchemas() } );
+ }
- remove(schema: Schema) {
- this.schemasService.rmSchema(schema).subscribe(
- result => {if (result['success']) { this.getSchemas()}});
- }
+ remove( schema: Schema ) {
+ this.schemasService.rmSchema( schema ).subscribe(
+ result => { if ( result['success'] ) { this.getSchemas() } } );
+ }
- ngOnInit(): void {
- this.getSchemas();
- }
+ ngOnInit(): void {
+ this.getSchemas();
+ }
- onSelect(schema: Schema): void {
- this.selectedSchema = schema;
- }
+ onSelect( schema: Schema ): void {
+ this.schemasService.show(schema);
+ this.schemasService.changeActiveSchema(schema.key);
+ this.router.navigateByUrl( '/netopeer/yang' );
+ }
}
diff --git a/frontend/netopeer.module.ts b/frontend/netopeer.module.ts
index fd5f2f0..72f3097 100644
--- a/frontend/netopeer.module.ts
+++ b/frontend/netopeer.module.ts
@@ -26,6 +26,7 @@
import { PluginsComponent } from './plugins/plugins.component';
import { SessionsService } from './config/sessions.service'
+import { SchemasService } from './yang/schemas.service'
const routes: Routes = [
{ path : 'netopeer', component : NetopeerComponent, canActivate : [AuthGuard],
@@ -41,6 +42,10 @@
canActivate : [AuthGuard],
data : { role : 10, name : 'Netopeer Items Inventories'},
children : [{
+ path : '',
+ redirectTo: 'devices',
+ pathMatch: 'full',
+ }, {
path : 'devices',
component : InventoryDevicesComponent,
canActivate : [AuthGuard],
@@ -104,7 +109,8 @@
PluginsComponent
],
providers: [
- SessionsService
+ SessionsService,
+ SchemasService
],
entryComponents : [
NetopeerComponent
diff --git a/frontend/yang/schemas.service.ts b/frontend/yang/schemas.service.ts
index 1407c59..92bfba5 100644
--- a/frontend/yang/schemas.service.ts
+++ b/frontend/yang/schemas.service.ts
@@ -1,35 +1,104 @@
import { Injectable } from '@angular/core';
-import { Http, Response, Headers, RequestOptions } from '@angular/http';
+import { Http, Headers, Response, RequestOptions, URLSearchParams } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
-import { Schema } from './schema';
+import { Schema } from '../inventory/schema';
@Injectable()
export class SchemasService {
- constructor(private http: Http) {}
+ public schemas: Schema[];
+ public activeSchema: string;
- getSchemas(): Observable<Schema[]> {
- return this.http.get('/netopeer/inventory/schemas/list')
- .map((resp: Response) => resp.json())
- .catch((err: Response | any) => Observable.throw(err));
- }
+ constructor( private http: Http ) {
+ this.loadData();
+ this.activeSchema = localStorage.getItem('activeSchema');
+ if (!this.activeSchema) {
+ this.activeSchema = "";
+ this.schemas = [];
+ }
+ }
- addSchema(schema: File) {
- let headers = new Headers({'specific-content-type': ''});
- let options = new RequestOptions({ headers: headers });
- let input = new FormData();
- input.append("schema", schema);
- return this.http.post('/netopeer/inventory/schemas', input, options)
- .map((resp: Response) => resp.json())
- .catch((err: Response | any) => Observable.throw(err));
- }
+ storeData() {
+ localStorage.setItem('schemas', JSON.stringify(this.schemas));
+ }
- rmSchema(schema: Schema) {
- let options = new RequestOptions({ body: JSON.stringify(schema) });
- return this.http.delete('/netopeer/inventory/schemas', options)
- .map((resp: Response) => resp.json())
- .catch((err: Response | any) => Observable.throw(err));
- }
+ loadData() {
+ this.schemas = JSON.parse(localStorage.getItem('schemas'));
+ }
+
+ getActiveSchema(key: string = this.activeSchema): Schema {
+ if (!key) {
+ return null;
+ }
+ for (let i = this.schemas.length; i > 0; i--) {
+ if (this.schemas[i - 1].key == key) {
+ return this.schemas[i - 1];
+ }
+ }
+ return null;
+ }
+
+ changeActiveSchema(key: string): Schema {
+ let result = this.getActiveSchema(key);
+ if (result) {
+ this.activeSchema = key;
+ localStorage.setItem('activeSession', this.activeSchema);
+ }
+ return result;
+ }
+
+ getSchemas() {
+ return this.http.get( '/netopeer/inventory/schemas' )
+ .map(( resp: Response ) => resp.json()).toPromise();
+ }
+
+ show(schema: Schema) {
+ let newSchema = true;
+ for (let i in this.schemas) {
+ if (this.schemas[i].key == schema.key) {
+ schema = this.schemas[i];
+ newSchema = false;
+ break;
+ }
+ }
+
+ if (!('data' in schema)) {
+ let params = new URLSearchParams();
+ params.set('key', schema.key);
+ let options = new RequestOptions({ search: params });
+ this.http.get('/netopeer/inventory/schema', options)
+ .map((resp: Response) => resp.json()).toPromise().then(result => {
+ console.log(result)
+ if (result['success']) {
+ schema['data'] = result['data'];
+ this.storeData();
+ console.log(this.schemas)
+ }
+ });
+ }
+
+ if (newSchema) {
+ this.schemas.push(schema);
+ this.storeData();
+ }
+ }
+
+ addSchema( schema: File ) {
+ let headers = new Headers( { 'specific-content-type': '' } );
+ let options = new RequestOptions( { headers: headers } );
+ let input = new FormData();
+ input.append( "schema", schema );
+ return this.http.post( '/netopeer/inventory/schemas', input, options )
+ .map(( resp: Response ) => resp.json() )
+ .catch(( err: Response | any ) => Observable.throw( err ) );
+ }
+
+ rmSchema( schema: Schema ) {
+ let options = new RequestOptions( { body: schema.key } );
+ return this.http.delete( '/netopeer/inventory/schemas', options )
+ .map(( resp: Response ) => resp.json() )
+ .catch(( err: Response | any ) => Observable.throw( err ) );
+ }
}
diff --git a/frontend/yang/yang.component.html b/frontend/yang/yang.component.html
index 7bd403c..a3a3543 100644
--- a/frontend/yang/yang.component.html
+++ b/frontend/yang/yang.component.html
@@ -1,10 +1,18 @@
-<div class="netopeer-content">
+<nav #subnav id="subnav">
+ <a *ngFor="let schema of schemasService.schemas" [class.active]="schema.key==activeSchema.key"
+ (click)="changeActiveSchema(schema.key)">{{schema.name}}@{{schema.revision}}
+ <img class="tab-icon tab-close tab-action-last" src="assets/netopeer/icons/close.svg" alt="x" title="close"
+ onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/close.svg'" (click)="close(schema.key)"/>
+ </a><a (click)="addSchema()" onmouseout="getElementById('tabadd').style.filter='invert(100%)'" onmouseover="getElementById('tabadd').style.filter='invert(0%)'">
+ <img id="tabadd" class="tab-icon tab-add tab-action-last" src="assets/netopeer/icons/add.svg" alt="+" title="open schema"
+ onmouseover="this.src='assets/netopeer/icons/add_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/add.svg'"/>
+ </a>
+</nav>
- <p>YANG Explorer (TBD)</p>
- <p>YANG schema browser</p>
- <ul>
- <li>navigate through the schemas (go to type definition, leafref target, etc)</li>
- </ul>
+<div class="netopeer-content" [style.padding-top]="'calc(' + subnav.offsetHeight + 'px - -0.7em)'">
+ <pre *ngIf="activeSchema">{{activeSchema.data}}</pre>
</div>
\ No newline at end of file
diff --git a/frontend/yang/yang.component.scss b/frontend/yang/yang.component.scss
index 12dd39c..a99b973 100644
--- a/frontend/yang/yang.component.scss
+++ b/frontend/yang/yang.component.scss
@@ -1 +1,12 @@
-@import '../netopeer-common';
\ No newline at end of file
+@import '../netopeer-common';
+@import '../inventory/inventory.component';
+
+.loading {
+ text-align: center;
+ margin: auto;
+ width: 10em;
+ div {
+ margin: auto;
+ width: 50px;
+ }
+}
diff --git a/frontend/yang/yang.component.ts b/frontend/yang/yang.component.ts
index de0fac9..388c7ae 100644
--- a/frontend/yang/yang.component.ts
+++ b/frontend/yang/yang.component.ts
@@ -1,4 +1,8 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
+import {Router} from '@angular/router';
+
+import {SchemasService} from './schemas.service';
+import {Schema} from '../inventory/schema';
@Component({
selector : 'netopeer-yang',
@@ -6,6 +10,41 @@
styleUrls : ['./yang.component.scss']
})
-export class YANGComponent {
+export class YANGComponent implements OnInit {
title = 'YANG Explorer';
+ activeSchema: Schema;
+
+ constructor(private schemasService: SchemasService,
+ private router: Router) {}
+
+
+ addSchema() {
+ this.router.navigateByUrl('/netopeer/inventory/schemas');
+ }
+
+ changeActiveSchema(key: string) {
+ this.activeSchema = this.schemasService.changeActiveSchema(key);
+ }
+
+ close(key: string) {
+ for (let i in this.schemasService.schemas) {
+ if (this.schemasService.schemas[i].key == key) {
+ this.schemasService.schemas.splice(Number(i), 1);
+ if (this.schemasService.activeSchema == key) {
+ if (Number(i) > 0) {
+ this.changeActiveSchema(this.schemasService.schemas[Number(i) - 1].key)
+ } else if (this.schemasService.schemas.length) {
+ this.changeActiveSchema(this.schemasService.schemas[0].key)
+ }
+ }
+ this.schemasService.storeData();
+ break;
+ }
+ }
+ this.activeSchema = this.schemasService.getActiveSchema();
+ }
+
+ ngOnInit(): void {
+ this.activeSchema = this.schemasService.getActiveSchema();
+ }
}