Unverified Commit 2bc301d8 authored by Pavel Spirek's avatar Pavel Spirek
Browse files

Initial commit

parents
.. |date| date::
*******
JetConf example Jukebox backend
*******
:Author: Pavel Špírek <pavel.spirek@nic.cz>
:Date: |date|
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)
from setuptools import setup, find_packages
import codecs
import os
here = os.path.abspath(os.path.dirname(__file__))
with codecs.open(os.path.join(here, 'README.rst'), encoding='utf-8') as readme:
long_description = readme.read()
setup(
name = "jetconf",
packages = find_packages(),
use_scm_version = True,
setup_requires=["setuptools_scm"],
description = "Pure Python implementation of RESTCONF server",
long_description = long_description,
url = "https://gitlab.labs.nic.cz/labs/jetconf",
author = "Pavel Spirek",
author_email = "pavel.spirek@nic.cz",
entry_points = {
"console_scripts": ["jetconf=jetconf.__main__:main"]
},
install_requires = ["yangson", "h2", "colorlog", "pyaml", "pytz"],
tests_require = ["pytest"],
keywords = ["RESTCONF", "yang", "data model", "configuration", "json"],
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3.5",
"Development Status :: 3 - Alpha",
"Intended Audience :: System Administrators",
"Intended Audience :: Telecommunications Industry",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
"Topic :: System :: Monitoring",
"Topic :: System :: Systems Administration"
]
)
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.";
}
}
}
}
module ietf-inet-types {
namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
prefix "inet";
organization
"IETF NETMOD (NETCONF Data Modeling Language) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
WG Chair: David Kessens
<mailto:david.kessens@nsn.com>
WG Chair: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Editor: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>";
description
"This module contains a collection of generally useful derived
YANG data types for Internet addresses and related things.
Copyright (c) 2013 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 6991; see
the RFC itself for full legal notices.";
revision 2013-07-15 {
description
"This revision adds the following new data types:
- ip-address-no-zone
- ipv4-address-no-zone
- ipv6-address-no-zone";
reference
"RFC 6991: Common YANG Data Types";
}
revision 2010-09-24 {
description
"Initial revision.";
reference
"RFC 6021: Common YANG Data Types";
}
/*** collection of types related to protocol fields ***/
typedef ip-version {
type enumeration {
enum unknown {
value "0";
description
"An unknown or unspecified version of the Internet
protocol.";
}
enum ipv4 {
value "1";
description
"The IPv4 protocol as defined in RFC 791.";
}
enum ipv6 {
value "2";
description
"The IPv6 protocol as defined in RFC 2460.";
}
}
description
"This value represents the version of the IP protocol.
In the value set and its semantics, this type is equivalent
to the InetVersion textual convention of the SMIv2.";
reference
"RFC 791: Internet Protocol
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
RFC 4001: Textual Conventions for Internet Network Addresses";
}
typedef dscp {
type uint8 {
range "0..63";
}
description
"The dscp type represents a Differentiated Services Code Point
that may be used for marking packets in a traffic stream.
In the value set and its semantics, this type is equivalent
to the Dscp textual convention of the SMIv2.";
reference
"RFC 3289: Management Information Base for the Differentiated
Services Architecture
RFC 2474: Definition of the Differentiated Services Field
(DS Field) in the IPv4 and IPv6 Headers
RFC 2780: IANA Allocation Guidelines For Values In
the Internet Protocol and Related Headers";
}
typedef ipv6-flow-label {
type uint32 {
range "0..1048575";
}
description
"The ipv6-flow-label type represents the flow identifier or Flow
Label in an IPv6 packet header that may be used to
discriminate traffic flows.
In the value set and its semantics, this type is equivalent
to the IPv6FlowLabel textual convention of the SMIv2.";
reference
"RFC 3595: Textual Conventions for IPv6 Flow Label
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
}
typedef port-number {
type uint16 {
range "0..65535";
}
description
"The port-number type represents a 16-bit port number of an
Internet transport-layer protocol such as UDP, TCP, DCCP, or
SCTP. Port numbers are assigned by IANA. A current list of
all assignments is available from <http://www.iana.org/>.
Note that the port number value zero is reserved by IANA. In
situations where the value zero does not make sense, it can
be excluded by subtyping the port-number type.
In the value set and its semantics, this type is equivalent
to the InetPortNumber textual convention of the SMIv2.";
reference
"RFC 768: User Datagram Protocol
RFC 793: Transmission Control Protocol
RFC 4960: Stream Control Transmission Protocol
RFC 4340: Datagram Congestion Control Protocol (DCCP)
RFC 4001: Textual Conventions for Internet Network Addresses";
}
/*** collection of types related to autonomous systems ***/
typedef as-number {
type uint32;
description
"The as-number type represents autonomous system numbers
which identify an Autonomous System (AS). An AS is a set
of routers under a single technical administration, using
an interior gateway protocol and common metrics to route
packets within the AS, and using an exterior gateway
protocol to route packets to other ASes. IANA maintains
the AS number space and has delegated large parts to the
regional registries.
Autonomous system numbers were originally limited to 16