Backends splitted from main server to separate packages

parent a2e349de
......@@ -5,6 +5,7 @@ import logging
import sys
import signal
from importlib import import_module
from colorlog import error, info
from yaml.parser import ParserError
......@@ -12,12 +13,14 @@ from yangson.enumerations import ContentType, ValidationScope
from yangson.exceptions import YangsonException
from yangson.schemanode import SchemaError, SemanticError
from . import op_internal, usr_state_data_handlers, usr_conf_data_handlers, usr_op_handlers
from .usr_datastore import UserDatastore
from . import op_internal
from .rest_server import RestServer
from .config import CONFIG_GLOBAL, CONFIG_NACM, load_config, print_config
from .helpers import DataHelpers, ErrorHelpers
# from jetconf_jukebox import usr_state_data_handlers, usr_conf_data_handlers, usr_op_handlers
# from jetconf_jukebox.usr_datastore import UserDatastore
def main():
config_file = "config.yaml"
......@@ -126,6 +129,18 @@ def main():
signal.signal(signal.SIGTERM, sig_exit_handler)
signal.signal(signal.SIGINT, sig_exit_handler)
# Import backend modules
backend_package = CONFIG_GLOBAL["BACKEND_PACKAGE"]
try:
usr_state_data_handlers = import_module(backend_package + ".usr_state_data_handlers")
usr_conf_data_handlers = import_module(backend_package + ".usr_conf_data_handlers")
usr_op_handlers = import_module(backend_package + ".usr_op_handlers")
usr_datastore = import_module(backend_package + ".usr_datastore")
except ImportError as e:
error(ErrorHelpers.epretty(e))
error("Cannot import backend package \"{}\". Exiting.".format(backend_package))
sys.exit(1)
# Load data model
yang_lib_file = os.path.join(CONFIG_GLOBAL["YANG_LIB_DIR"], "yang-library-data.json")
datamodel = DataHelpers.load_data_model(
......@@ -134,7 +149,7 @@ def main():
)
# Datastore init
datastore = UserDatastore(datamodel, CONFIG_GLOBAL["DATA_JSON_FILE"], with_nacm=CONFIG_NACM["ENABLED"])
datastore = usr_datastore.UserDatastore(datamodel, CONFIG_GLOBAL["DATA_JSON_FILE"], with_nacm=CONFIG_NACM["ENABLED"])
try:
datastore.load()
datastore.load_yl_data(yang_lib_file)
......
......@@ -12,7 +12,8 @@ CONFIG_GLOBAL = {
"LOG_DBG_MODULES": ["*"],
"YANG_LIB_DIR": "yang-data/",
"DATA_JSON_FILE": "data.json",
"VALIDATE_TRANSACTIONS": True
"VALIDATE_TRANSACTIONS": True,
"BACKEND_PACKAGE": "jetconf_jukebox"
}
CONFIG_HTTP = {
......
from colorlog import info
from typing import List, Dict, Union, Any
from yangson.instance import InstanceRoute
from .data import BaseDatastore, DataChange
from .helpers import ErrorHelpers, LogHelpers
from .handler_list import CONF_DATA_HANDLES, ConfDataObjectHandler, ConfDataListHandler
JsonNodeT = Union[Dict[str, Any], List]
epretty = ErrorHelpers.epretty
debug_confh = LogHelpers.create_module_dbg_logger(__name__)
# ---------- User-defined handlers follow ----------
class JukeboxExampleConfHandler(ConfDataListHandler):
def create_item(self, ii: InstanceRoute, ch: "DataChange"):
debug_confh(self.__class__.__name__ + " replace triggered")
info("Creating item '/example-jukebox:jukebox/library/artist' in app configuration")
def create_list(self, ii: InstanceRoute, ch: "DataChange"):
debug_confh(self.__class__.__name__ + " replace triggered")
info("Creating list '/example-jukebox:jukebox/library/artist' in app configuration")
def replace_item(self, ii: InstanceRoute, ch: "DataChange"):
debug_confh(self.__class__.__name__ + " replace triggered")
info("Replacing item '/example-jukebox:jukebox/library/artist' in app configuration")
def replace_list(self, ii: InstanceRoute, ch: "DataChange"):
debug_confh(self.__class__.__name__ + " replace triggered")
info("Replacing list '/example-jukebox:jukebox/library/artist' in app configuration")
def delete_item(self, ii: InstanceRoute, ch: DataChange):
debug_confh(self.__class__.__name__ + " delete triggered")
info("Deleting item '/example-jukebox:jukebox/library/artist' from app configuration")
def delete_list(self, ii: InstanceRoute, ch: "DataChange"):
debug_confh(self.__class__.__name__ + " delete triggered")
info("Deleting list '/example-jukebox:jukebox/library/artist' from app configuration")
def register_conf_handlers(ds: BaseDatastore):
CONF_DATA_HANDLES.register(JukeboxExampleConfHandler(ds, "/example-jukebox:jukebox/library/artist"))
from yangson.datamodel import DataModel
from .data import JsonDatastore
class UserDatastore(JsonDatastore):
def __init__(self, dm: DataModel, json_file: str, with_nacm: bool = False):
super().__init__(dm, json_file, with_nacm)
self.name = "Example Data"
# Application-specific init actions can be defined here
from colorlog import info
from .helpers import JsonNodeT
from .handler_list import OP_HANDLERS
from .data import BaseDatastore
# ---------- User-defined handlers follow ----------
class OpHandlersContainer:
def __init__(self, ds: BaseDatastore):
self.ds = ds
def jukebox_play_op(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
# Structure of RPC's input and output arguments is defined in YANG data model
# Do something
info("Called operation 'jukebox_play_op' by user '{}':".format(username))
info("Playlist name: {}".format(input_args["example-jukebox:playlist"]))
info("Song number: {}".format(input_args["example-jukebox:song-number"]))
def register_op_handlers(ds: BaseDatastore):
op_handlers_obj = OpHandlersContainer(ds)
OP_HANDLERS.register(op_handlers_obj.jukebox_play_op, "example-jukebox:play")
from colorlog import error, warning as warn, info
from yangson.instance import InstanceRoute
from .helpers import JsonNodeT, PathFormat
from .handler_list import STATE_DATA_HANDLES, StateDataContainerHandler
from .data import BaseDatastore
# ---------- User-defined handlers follow ----------
# This handler will generate /example-jukebox:jukebox/library/artist-count node
class JukeboxExampleStateHandler(StateDataContainerHandler):
def generate_node(self, node_ii: InstanceRoute, username: str, staging: bool) -> JsonNodeT:
info("jukebox_example_handler, ii = {}".format(node_ii))
artist_list_ii = self.ds.parse_ii("/example-jukebox:jukebox/library/artist", PathFormat.URL)
jb_artists = self.ds.get_data_root().goto(artist_list_ii).value
return len(jb_artists)
# This handler will generate /example-jukebox:jukebox/library/album-count node
class JukeboxExampleStateHandlerAc(StateDataContainerHandler):
def generate_node(self, node_ii: InstanceRoute, username: str, staging: bool) -> JsonNodeT:
info("jukebox_example_handler_ac, ii = {}".format(node_ii))
artist_list_ii = self.ds.parse_ii("/example-jukebox:jukebox/library/artist", PathFormat.URL)
jb_artists = self.ds.get_data_root().goto(artist_list_ii).value
album_count = 0
for artist in jb_artists:
album_list = artist.get("album", [])
album_count += len(album_list)
return album_count
# This handler will generate /example-jukebox:jukebox/library/song-count node
class JukeboxExampleStateHandlerSc(StateDataContainerHandler):
def generate_node(self, node_ii: InstanceRoute, username: str, staging: bool) -> JsonNodeT:
info("jukebox_example_handler_sc, ii = {}".format(node_ii))
artist_list_ii = self.ds.parse_ii("/example-jukebox:jukebox/library/artist", PathFormat.URL)
jb_artists = self.ds.get_data_root().goto(artist_list_ii).value
song_count = 0
for artist in jb_artists:
album_list = artist.get("album", [])
for album in album_list:
song_list = album.get("song", [])
song_count += len(song_list)
return song_count
# Instantiate state data handlers
def register_state_handlers(ds: BaseDatastore):
esh = JukeboxExampleStateHandler(ds, "/example-jukebox:jukebox/library/artist-count")
esh_ac = JukeboxExampleStateHandlerAc(ds, "/example-jukebox:jukebox/library/album-count")
esh_sc = JukeboxExampleStateHandlerSc(ds, "/example-jukebox:jukebox/library/song-count")
STATE_DATA_HANDLES.register(esh)
STATE_DATA_HANDLES.register(esh_ac)
STATE_DATA_HANDLES.register(esh_sc)
module example-jukebox {
namespace "http://example.com/ns/example-jukebox";
prefix "jbox";
organization "Example, Inc.";
contact "support at example.com";
description "Example Jukebox Data Model Module.";
revision "2016-08-15" {
description "Initial version.";
reference "example.com document 1-4673.";
}
identity genre {
description
"Base for all genre types.";
}
// abbreviated list of genre classifications
identity alternative {
base genre;
description
"Alternative music.";
}
identity blues {
base genre;
description
"Blues music.";
}
identity country {
base genre;
description
"Country music.";
}
identity jazz {
base genre;
description
"Jazz music.";
}
identity pop {
base genre;
description
"Pop music.";
}
identity rock {
base genre;
description
"Rock music.";
}
container jukebox {
presence
"An empty container indicates that the jukebox
service is available.";
description
"Represents a 'jukebox' resource, with a library, playlists,
and a 'play' operation.";
container library {
description
"Represents the 'jukebox' library resource.";
list artist {
key name;
description
"Represents one 'artist' resource within the
'jukebox' library resource.";
leaf name {
type string {
length "1 .. max";
}
description
"The name of the artist.";
}
list album {
key name;
description
"Represents one 'album' resource within one
'artist' resource, within the jukebox library.";
leaf name {
type string {
length "1 .. max";
}
description
"The name of the album.";
}
leaf genre {
type identityref { base genre; }
description
"The genre identifying the type of music on
the album.";
}
leaf year {
type uint16 {
range "1900 .. max";
}
description
"The year the album was released.";
}
container admin {
description
"Administrative information for the album.";
leaf label {
type string;
description
"The label that released the album.";
}
leaf catalogue-number {
type string;
description
"The album's catalogue number.";
}
}
list song {
key name;
description
"Represents one 'song' resource within one
'album' resource, within the jukebox library.";
leaf name {
type string {
length "1 .. max";
}
description
"The name of the song.";
}
leaf location {
type string;
mandatory true;
description
"The file location string of the
media file for the song.";
}
leaf format {
type string;
description
"An identifier string for the media type
for the file associated with the
'location' leaf for this entry.";
}
leaf length {
type uint32;
units "seconds";
description
"The duration of this song in seconds.";
}
} // end list 'song'
} // end list 'album'
} // end list 'artist'
leaf artist-count {
type uint32;
units "artists";
config false;
description
"Number of artists in the library.";
}
leaf album-count {
type uint32;
units "albums";
config false;
description
"Number of albums in the library.";
}
leaf song-count {
type uint32;
units "songs";
config false;
description
"Number of songs in the library.";
}
} // end library
list playlist {
key name;
description
"Example configuration data resource.";
leaf name {
type string;
description
"The name of the playlist.";
}
leaf description {
type string;
description
"A comment describing the playlist.";
}
list song {
key index;
ordered-by user;
description
"Example nested configuration data resource.";
leaf index { // not really needed
type uint32;
description
"An arbitrary integer index for this playlist song.";
}
leaf id {
type instance-identifier;
mandatory true;
description
"Song identifier. Must identify an instance of
/jukebox/library/artist/album/song/name.";
}
}
}
container player {
description
"Represents the jukebox player resource.";
leaf gap {
type decimal64 {
fraction-digits 1;
range "0.0 .. 2.0";
}
units "tenths of seconds";
description
"Time gap between each song.";
}
}
}
rpc play {
description
"Control function for the jukebox player.";
input {
leaf playlist {
type string;
mandatory true;
description
"The playlist name.";
}
leaf song-number {
type uint32;
mandatory true;
description
"Song number in playlist to play.";
}
}
}
}
This diff is collapsed.
This diff is collapsed.
module ietf-yang-library {
namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library";
prefix "yanglib";
import ietf-yang-types {
prefix yang;
}
import ietf-inet-types {
prefix inet;
}
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
WG Chair: Mehmet Ersue
<mailto:mehmet.ersue@nsn.com>
WG Chair: Mahesh Jethanandani
<mailto:mjethanandani@gmail.com>
Editor: Andy Bierman
<mailto:andy@yumaworks.com>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>
Editor: Kent Watsen
<mailto:kwatsen@juniper.net>";
description
"This module contains monitoring information about the YANG
modules and submodules that are used within a YANG-based
server.
Copyright (c) 2016 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 7895; see
the RFC itself for full legal notices.";
revision 2016-06-21 {
description
"Initial revision.";
reference
"RFC 7895: YANG Module Library.";
}
/*
* Typedefs
*/
typedef revision-identifier {
type string {
pattern '\d{4}-\d{2}-\d{2}';
}
description
"Represents a specific date in YYYY-MM-DD format.";
}
/*
* Groupings
*/
grouping module-list {
description
"The module data structure is represented as a grouping
so it can be reused in configuration or another monitoring
data structure.";
grouping common-leafs {
description
"Common parameters for YANG modules and submodules.";
leaf name {
type yang:yang-identifier;
description
"The YANG module or submodule name.";
}
leaf revision {
type union {
type revision-identifier;
type string { length 0; }
}
description
"The YANG module or submodule revision date.
A zero-length string is used if no revision statement
is present in the YANG module or submodule.";
}
}
grouping schema-leaf {
description
"Common schema leaf parameter for modules and submodules.";
leaf schema {
type inet:uri;
description
"Contains a URL that represents the YANG schema
resource for this module or submodule.
This leaf will only be present if there is a URL
available for retrieval of the schema for this entry.";
}
}
list module {
key "name revision";
description
"Each entry represents one revision of one module
currently supported by the server.";
uses common-leafs;
uses schema-leaf;
leaf namespace {
type inet:uri;
mandatory true;
description
"The XML namespace identifier for this module.";
}
leaf-list feature {
type yang:yang-identifier;
description
"List of YANG feature names from this module that are
supported by the server, regardless of whether they are
defined in the module or any included submodule.";
}
list deviation {
key "name revision";
description
"List of YANG deviation module names and revisions
used by this server to modify the conformance of
the module associated with this entry. Note that
the same module can be used for deviations for
multiple modules, so the same entry MAY appear
within multiple 'module' entries.
The deviation module MUST be present in the 'module'
list, with the same name and revision values.
The 'conformance-type' value will be 'implement' for
the deviation module.";
uses common-leafs;
}
leaf conformance-type {
type enumeration {
enum implement {
description
"Indicates that the server implements one or more
protocol-accessible objects defined in the YANG module
identified in this entry. This includes deviation
statements defined in the module.
For YANG version 1.1 modules, there is at most one
module entry with conformance type 'implement' for a
particular module name, since YANG 1.1 requires that,
at most, one revision of a module is implemented.
For YANG version 1 modules, there SHOULD NOT be more
than one module entry for a particular module name.";
}
enum import {
description
"Indicates that the server imports reusable definitions
from the specified revision of the module but does
not implement any protocol-accessible objects from
this revision.
Multiple module entries for the same module name MAY
exist. This can occur if multiple modules import the
same module but specify different revision dates in
the import statements.";
}
}
mandatory true;
description
"Indicates the type of conformance the server is claiming
for the YANG module identified by this entry.";
}
list submodule {
key "name revision";
description
"Each entry represents one submodule within the
parent module.";
uses common-leafs;
uses schema-leaf;
}
}
}
/*
* Operational state data nodes
*/
container modules-state {
config false;
description
"Contains YANG module monitoring information.";
leaf module-set-id {
type string;
mandatory true;
description
"Contains a server-specific identifier representing
the current set of modules and submodules. The
server MUST change the value of this leaf if the
information represented by the 'module' list instances
has changed.";
}
uses module-list;
}
/*
* Notifications
*/
notification yang-library-change {
description
"Generated when the set of modules and submodules supported
by the server has changed.";
leaf module-set-id {
type leafref {
path "/yanglib:modules-state/yanglib:module-set-id";
}
mandatory true;
description
"Contains the module-set-id value representing the
set of modules and submodules supported at the server at
the time the notification is generated.";
}
}
}
This diff is collapsed.
{
"ietf-yang-library:modules-state": {
"module-set-id": "e595da11ace92c0d881995fa7e56bbe86f1f48e9",
"module": [
{
"name": "ietf-inet-types",
"revision": "2013-07-15",
"namespace": "urn:ietf:params:xml:ns:yang:ietf-inet-types",
"conformance-type": "import",
"schema": "https://raw.githubusercontent.com/YangModels/yang/master/standard/ietf/RFC/ietf-inet-types.yang"
},
{
"name": "ietf-netconf-acm",
"revision": "2012-02-22",
"namespace": "urn:ietf:params:xml:ns:yang:ietf-netconf-acm",
"conformance-type": "implement",
"schema": "https://raw.githubusercontent.com/YangModels/yang/master/standard/ietf/RFC/ietf-netconf-acm.yang"
},
{
"name": "ietf-yang-library",
"revision": "2016-06-21",
"namespace": "urn:ietf:params:xml:ns:yang:ietf-yang-library",
"conformance-type": "implement"
},
{
"name": "ietf-yang-types",
"revision": "2013-07-15",
"namespace": "urn:ietf:params:xml:ns:yang:ietf-yang-types",
"conformance-type": "import",
"schema": "https://github.com/YangModels/yang/blob/master/standard/ietf/RFC/ietf-yang-types.yang"
},
{
"name": "example-jukebox",
"revision": "2016-08-15",
"namespace": "urn:ietf:params:xml:ns:yang:example-jukebox",
"conformance-type": "implement",
"schema": "https://github.com/YangModels/yang/blob/master/standard/ietf/DRAFT/example-jukebox.yang"
}
]
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment