Merge remote-tracking branch 'github/master'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f85f310
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules
+jspm_packages
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+*~
+
+# ignore generated *.js files inside lib and dist
+lib/*
+dist/*
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..a7ed084
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+.git*
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cbbbe22
--- /dev/null
+++ b/README.md
@@ -0,0 +1,71 @@
+# YANG model-driven CORD
+
+This is a **work-in-progress** effort to create
+[YANG](http://tools.ietf.org/html/rfc6020) data models for the
+[CORD](http://opencord.org) project and deliver flexible service
+compositions.
+
+You may contact Larry Peterson <llp@onlab.us> and Peter Lee
+<peter@corenova.com> to learn more about this initiative and find out
+how you can help.
+
+## Installation
+
+```bash
+$ npm install yang-cord
+```
+
+## Getting Started
+
+Following the installation, you can **start** an instance of the YANG
+model-driven REST API web server. It utilizes
+[yang-express](http://github.com/corenova/yang-express) middleware
+framework built on [Express.js](http://expressjs.com) to provide
+dynamic YANG model-driven API routing capability.
+
+```bash
+$ npm start
+
+> yang-cord@1.0.8 start /home/plee/hack/yang-cord
+> node lib/api/server.js
+
+[yang-express] start of a new journey
+[openapi] enabling...
+[openapi] enabled ok
+[restjson] enabling...
+[restjson] enabled ok
+[websocket] enabling...
+[yang-express] registering a new link
+[yang-express] registered 'link:cord-core'
+[yang-express] registering a new link
+[yang-express] registered 'link:xos-core'
+[websocket] binding to server
+[websocket] enabled ok
+```
+
+_An option `--port` is provided to specify the port to listen on, it can be used with:_
+
+```
+npm start -- --port 3000
+```
+
+## Reference Guides
+
+- [API Guide](./src/README.md) - provides a walkthrough on *interacting with the REST API endpoints*
+- [Modeler's Guide](./schema/README.md) - provides information on *current YANG models for XOS and CORD* and what's coming up next
+- [Developer's Guide](./src/README.md) - provides technical detail on *controller logic and dynamic interfaces* and how to best leverage YANG model-driven developer tools for getting things done *fast*.
+
+## Tests
+
+To run the test suite, first install the dependencies, then run `npm
+test`.
+
+```
+$ npm install
+$ npm test
+```
+Mocha test suite is currently under development...
+
+## LICENSE
+ [Apache 2.0](LICENSE)
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d72531f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,61 @@
+{
+ "name": "yang-cord",
+ "version": "1.1.4",
+ "description": "YANG model-driven CORD",
+ "author": "Peter K. Lee <peter@corenova.com>",
+ "license": "Apache-2.0",
+ "homepage": "http://wiki.opencord.org/",
+ "repository": "corenova/yang-cord",
+ "keywords": [
+ "cord",
+ "opencord",
+ "onlab",
+ "xos",
+ "onos",
+ "subscriber",
+ "tenant",
+ "device",
+ "yangjs",
+ "express",
+ "service"
+ ],
+ "models": {
+ "ietf-yang-types": "yang-js",
+ "ietf-inet-types": "yang-js",
+ "corenova-node": "corenova",
+ "xos-core": "./schema/xos-core.yang",
+ "cord-core": "./lib/cord-core.js",
+ "cord-device": "./lib/cord-device.js",
+ "cord-subscriber": "./lib/cord-subscriber.js"
+ },
+ "dependencies": {
+ "minimist": "^1.2.0",
+ "node-uuid": "^1.4.7",
+ "superagent": "^2.0.0",
+ "yang-express": "^0.3.15",
+ "yang-js": "^0.15.25"
+ },
+ "optionalDependencies": {
+ "corenova": "^1.0.0"
+ },
+ "devDependencies": {
+ "coffee-script": ">=1.7.0",
+ "config": "^1.19.0",
+ "mocha": "~2.0.1",
+ "rimraf": "^2.5.2",
+ "should": "~3.1.3",
+ "webpack": "^1.13.1"
+ },
+ "main": "./lib/cord-core.js",
+ "scripts": {
+ "clean": "rimraf dist/* lib/*",
+ "prebuild": "npm run clean -s && mkdir -p dist",
+ "build:src": "coffee -o lib -c src",
+ "build:web": "webpack -p",
+ "build": "npm run build:src",
+ "prepublish": "npm run build",
+ "pretest": "npm run build",
+ "test": "mocha",
+ "start": "node lib/server.js"
+ }
+}
diff --git a/sample-data.json b/sample-data.json
new file mode 100644
index 0000000..b879e51
--- /dev/null
+++ b/sample-data.json
@@ -0,0 +1,10 @@
+{
+ "xos-core:api": {
+ "service": {},
+ "tenant": {}
+ },
+ "cord-core:subscriber": [
+ { "id": 1, "service-specific-id": 1000 },
+ { "id": 2, "service-specific-id": 1001 }
+ ]
+}
diff --git a/schema/README.md b/schema/README.md
new file mode 100644
index 0000000..3e6ecd8
--- /dev/null
+++ b/schema/README.md
@@ -0,0 +1,176 @@
+# Modeler's Guide
+
+This is also a **work-in-progress** documentation capturing current
+models for XOS and CORD. It's currently *exploratory* and contains
+free-flowing commentary regarding observations from reviewing the
+originating reference code repository as well as any deviations and
+recommendations on expressing them as YANG models.
+
+The initial effort is centered around capturing the CORD Subscriber
+model and its dependent models. We'll be reguarly updating this
+document as we capture additional models from XOS/CORD repository in
+the coming days.
+
+## XOS Data Models
+
+### xos-core
+
+- YANG schema: [xos-core.yang](./xos-core.yang)
+- Source reference: [opencord/xos](http://github.com/opencord/xos)
+
+This YANG module is the **primary** module that will house all XOS
+related data models going forward. The models for these came from
+`xos/core/models` directory in the XOS repository. It will eventually
+house the `Service` class, the `Tenant` class, etc. One notable
+convention here is the existence of the `/api/tenant` and
+`/api/service` configuration tree inside this module. In a sense,
+we're considering this YANG module to be the **master** module that
+all other modules derive from and augments into this module. You can
+see this *augment* behavior in the
+[cord-core.yang](./cord-core.yang) schema.
+
+As we capture more XOS data models, we will likely organize the
+additional models as separate YANG modules, such as `xos-service`,
+`xos-tenant`, `xos-slice`, etc. which will be *imported* by this
+module.
+
+For now, we've captured the `TenantRoot` and `Subscriber` classes.
+
+This module contains basic placeholder configuration data tree and
+passed into `yang-express`.
+
+## CORD Data Models
+
+### cord-core
+
+- YANG schema: [cord-core.yang](./cord-core.yang)
+- Source reference: [opencord](http://github.com/opencord)
+
+This YANG module is the **primary** module that will house all CORD
+related data models going forward. It currently captures the
+`cord-subscriber` configuration tree and *imports* the subscriber
+schema from the `cord-subscriber` YANG module. From a pure data
+modeling perspective, the current originating reference implementation
+has yet to achieve a clean functional separation between XOS and
+CORD. This will be a key area of focus as we attempt to define a clear
+degree of abstraction between the XOS models and CORD models.
+
+This module contains subscriber-related configuration data tree and
+passed into `yang-express`.
+
+There are two main entry-points on the `subscriber` instances. I've
+defined a `list subscriber` construct directly in the `module
+cord-core` which basically uses the `grouping subscriber-controller`
+data model. This means that the `cord-core` YANG module itself will
+be the authorative holder of all subscriber instances. Subsequently,
+I've augmented the `xos` module at the `/api/tenant` configuration
+tree to have a new `cord` tenant container along with a `node:link` to
+the subscriber list within the `cord-core` YANG module. This
+convention makes it possible to access the `subscriber` instances by
+directly accessing the `cord-core` module, such as
+`/cord-core:subscriber` or via the `xos` module, such as
+`/xos-core:api/tenant/cord/subscriber`.
+
+### cord-device
+
+- YANG schema: [cord-device.yang](./cord-device.yang)
+- Source reference: [opencord/olt](http://github.com/opencord/olt)
+
+This YANG module is based on the `CordDevice` class found inside the
+`subscriber.py` (API) module implementation. I think this particular
+model is rather under-developed and currently not placed in the right
+place (shouldn't be inside `subscriber.py` which should really just be
+the controller definitions). This module has a potential to be
+leveraged more effectively if the goal is for this to become one the
+the core CORD models from which other devices inherit from (which I'm
+guessing it will be).
+
+We will need to review its association with the `cord-subscriber`
+model and better understand its role in relation with other *device*
+oriented data models.
+
+This module provides *export definitions* and does not contain any
+configuration data tree.
+
+### cord-subscriber
+
+- YANG schema: [cord-subscriber.yang](./schema/cord-subscriber.yang)
+- Source reference: [opencord/olt](http://github.com/opencord/olt)
+
+This module contains the heart of the initial modeling exercise. It
+captures the CORD Subscriber data model (which extends XOS Subscriber
+model, which extends XOS TenantRoot model). There are two primary
+models: `grouping subscriber` and `grouping subscriber-controller`.
+
+This module provides *export definitions* and does not contain any
+configuration data tree.
+
+#### grouping subscriber
+
+The 'subscriber' model extends the `xos:subscriber` (from the
+[xos-core.yang](./xos-core.yang)) and mirrors as closely as possible
+the `CordSubscriberRoot` class. Some deviations were largely around
+the variable name convention where I've replaced all underscore with a
+dash (*upload_speed* is now *upload-speed*). This is largely to
+comply with YANG convention where the schema is used to model XML
+element structure and underscores are not really used in XML based
+representations (not even sure if it is valid...).
+
+The other key deviation is in the organization of the various
+attributes. Instead of having a simple flat list of properties, I've
+grouped them into related 'services' (pseudo-JSON below):
+
+```js
+services: {
+ cdn: {
+ enabled: true
+ },
+ firewall: {
+ enabled: true,
+ rules: []
+ },
+ url-filter: {
+ enabled: true,
+ level: 'PG',
+ rules: []
+ }
+ uverse: {
+ enabled: true
+ }
+}
+```
+
+Eventually, I think these four *hard-coded* services will be moved out
+of the `cord-subscriber` data model altogether. I'm not sure what
+*on-boarded* services actually implement these features but it should
+be augmented by those individual service's YANG models into the
+cord-subscriber data model.
+
+#### grouping subscriber-controller
+
+I've internally debated creating this controller model because I
+thought that the necessary attributes were rather effectively captured
+in the prior `grouping subscriber` schema definition. But the
+presence of the `related` object container in the controller (that
+shouldn't be in the underlying model) convinced me to model it
+according to the `CordSubscriberNew` class found inside
+`api/tenant/cord/subscriber.py`. This `subscriber-controller` model
+extends the `subscriber` model (inheriting all its attributes) and
+introduces the additional containers for `features`, `identity`, and
+`related`. Since the main function of the `subscriber-controller`
+model is to essentially layer a *view-like* overlay on top of the
+underlying cord-subscriber model, I've introduced a new *custom
+extension* construct called `node:link`. This is to capture the fact
+that the various attributes being expressed are simply a reference
+link to the exact same data that is located at a different place
+within the same object.
+
+One note on the `related` object, it is currently a placeholder
+container and I expect it will remain that way as part of the model.
+The reason is, the attributes that are currently mapped inside all
+come from the `VOLT` service (which then has other attributes from
+`VSG` service, etc.). When we get to the step of modeling the various
+`Service` entities, we'll capture the necessary *augment* behavior in
+those separate YANG modules (i.e. cord-service-volt.yang,
+cord-service-vsg.yang, etc.).
+
diff --git a/schema/cord-core.yang b/schema/cord-core.yang
new file mode 100644
index 0000000..18b9f47
--- /dev/null
+++ b/schema/cord-core.yang
@@ -0,0 +1,58 @@
+module cord-core {
+
+ namespace "urn:onlab:cord";
+ prefix cord;
+ yang-version 1.1;
+
+ organization
+ "Open Networking Lab (CORD) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ description
+ "This module contains a collection of core models for CORD.
+
+ Copyright (c) 2016 ON.LAB 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 of the Apache License, Version 2.0 which accompanies this
+ distribution, and is available at
+ (http://www.apache.org/licenses/LICENSE-2.0).";
+
+ revision 2016-07-22 {
+ description "Initial revision.";
+ }
+
+ import xos-core { prefix xos; }
+ import cord-subscriber { prefix csub; }
+ import corenova-node { prefix node; }
+
+ /*** primary configuration tree for this module ***/
+
+ list subscriber {
+ uses csub:subscriber;
+ key "id";
+
+ description
+ "Authorative list of all subscriber instances";
+
+ leaf label {
+ config false;
+ type string {
+ pattern '^cordSubscriber-\w+$';
+ }
+ }
+ action delete;
+ }
+
+ // here we augment the /api/tenant API configuration tree in 'xos' module
+ augment "/xos:api/xos:tenant" {
+ container cord {
+ node:link subscriber { path "/cord:subscriber"; }
+ }
+ }
+}
diff --git a/schema/cord-device.yang b/schema/cord-device.yang
new file mode 100644
index 0000000..c0916ff
--- /dev/null
+++ b/schema/cord-device.yang
@@ -0,0 +1,48 @@
+module cord-device {
+ namespace "urn:onlab:cord:device";
+ prefix cord-dev;
+ yang-version 1.1;
+
+ organization
+ "Open Networking Lab (CORD) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ import ietf-yang-types { prefix yang; }
+
+ revision 2016-07-14 {
+ description "Initial revision.";
+ }
+
+ typedef bandwidth {
+ type uint32 {
+ range 1000000..max; // should be at least 1 Mbps?
+ }
+ units 'bps';
+ }
+
+ grouping device {
+ leaf mac { type yang:mac-address; mandatory true; }
+ leaf identity { type string; }
+
+ leaf subscriber {
+ type instance-identifier;
+ config false;
+ }
+
+ container features {
+ // how are the uplink/downlink speeds defined here related to 'subscriber'?
+ leaf uplink-speed {
+ type bandwidth;
+ default 1000000000;
+ }
+ leaf downlink-speed {
+ type bandwidth;
+ default 1000000000;
+ }
+ action update;
+ }
+ }
+}
diff --git a/schema/cord-service-volt.yang b/schema/cord-service-volt.yang
new file mode 100644
index 0000000..3073366
--- /dev/null
+++ b/schema/cord-service-volt.yang
@@ -0,0 +1,167 @@
+module cord-service-volt {
+ namespace "urn:onlab:cord:service:volt";
+ prefix volt;
+ yang-version 1.1;
+
+ import ietf-yang-types { prefix yang; }
+ import xos-core { prefix xos; }
+
+ organization
+ "Open Networking Lab (CORD) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ revision 2016-09-09 {
+ description "Initial revision.";
+ }
+
+ identity volt-service { base xos:service; }
+
+ feature onos-app-olt {
+ description "System facility to configure VLAN tags on the OLT.";
+ }
+ feature onos-app-aaa {
+ description "System facility to broker authentication between CPE and Radius server.";
+ }
+
+ typedef bandwidth {
+ type xos:bandwidth;
+ default 1000000000; // VOLT bandwidth default is 1Gbps
+ }
+ typedef subscriber-flow {
+ type leafref {
+ path "/volt:service/volt:provider/volt:port/volt:id";
+ }
+ }
+
+ grouping devices-list {
+ grouping olt-device {
+ description
+ "This grouping describes an OLT device which contains ODN link attachments.";
+
+ leaf name {
+ description "name of OLT device";
+ type string {
+ length 1..254;
+ }
+ }
+ leaf mac { type yang:mac-address; mandatory true; }
+
+ container uplink {
+ description "Uplink description of the OLT device.";
+ leaf network { type yang:uuid; }
+ leaf tag {
+ if-feature onos-app-olt;
+ type xos:vlan;
+ description
+ "Represents S-Tag for instructing OLT to associate a VLAN tag for
+ traffic originating from OLT device.";
+ }
+ }
+
+ list link {
+ description
+ "Each link represents an ONU/ONT (Optical Network Termination) endpoint
+ connection.";
+
+ key serial;
+ unique tag;
+
+ leaf mac { type yang:mac-address; mandatory true; }
+ leaf serial { type string; mandatory true; }
+ leaf active { type boolean; default false; }
+ leaf tag {
+ if-feature onos-app-olt;
+ type xos:vlan;
+ description
+ "Represents C-Tag for instructing ONT to add/remove vlan tag for
+ traffic within OLT device.";
+ }
+ }
+ }
+ list device {
+ description
+ "Each entry represents an OLT device.";
+ key mac;
+ //unique 'uplink/tag';
+ uses olt-device;
+ }
+ }
+ grouping provider {
+ description
+ "This grouping represents a VOLT agent/provider which manages multiple
+ OLT devices. The VOLT agent provides aggregate abstraction of
+ the entire PON as a sigle switch to the controller. Each port
+ entry of the agent represents each ONU/ONT endpoint as a
+ separate openflow port.";
+
+ uses devices-list {
+ description
+ "Each entry represents an OLT device managed by the agent.";
+ }
+ list port {
+ description
+ "Each entry represents an ONU/ONT endpoint connected across OLT devices.";
+ key id;
+ leaf id {
+ description "OpenFlow Port ID";
+ type yang:uuid;
+ mandatory true;
+ }
+ leaf link {
+ type leafref {
+ path '../../device/link/serial';
+ }
+ mandatory true;
+ }
+ }
+ container radius {
+ if-feature onos-app-aaa;
+ // configuration for how to broker authentication requests
+ }
+ }
+ grouping subscriber {
+ description
+ "This grouping represents a VOLT service subscriber along with
+ references to ONU/ONT access endpoints used by the subscriber.";
+
+ list tag {
+ description
+ "Each entry represents a unique combination of the OLT uplink VLAN
+ (outer tag) and the ONU/ONT link VLAN (inner tag) connecting
+ into the fabric for the subscriber.";
+
+ key 'outer inner';
+ leaf outer {
+ type leafref {
+ path "/volt:service/volt:provider/volt:device/volt:uplink/volt:tag";
+ }
+ }
+ leaf inner {
+ type leafref {
+ path "/volt:service/volt:provider/volt:device/volt:link/volt:tag";
+ }
+ }
+ }
+ leaf-list flows {
+ description
+ "Each entry represents a unique openflow port ID that the subscriber
+ uses to connect into the fabric from the VOLT service.";
+ config false;
+ type subscriber-flow;
+ }
+ }
+
+ /*
+ * Configuration data
+ */
+ container service {
+ uses xos:service {
+ refine kind { default volt-service; }
+ augment "provider" { uses volt:provider; }
+ augment "subscriber" { uses volt:subscriber; }
+ }
+ }
+}
diff --git a/schema/cord-service-vsg.yang b/schema/cord-service-vsg.yang
new file mode 100644
index 0000000..16615e8
--- /dev/null
+++ b/schema/cord-service-vsg.yang
@@ -0,0 +1,157 @@
+module cord-service-vsg {
+ namespace "urn:onlab:cord:service:volt";
+ prefix volt;
+ yang-version 1.1;
+
+ import ietf-yang-types { prefix yang; }
+ import xos-core { prefix xos; }
+ import cord-service-volt { prefix volt; }
+
+ organization
+ "Open Networking Lab (CORD) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ revision 2016-09-09 {
+ description "Initial revision.";
+ }
+
+ identity vsg-service { base xos:service; }
+
+ typedef subscriber-flow {
+ type leafref {
+ path "/vsg:service/vsg:provider/vsg:port/vsg:id";
+ }
+ }
+
+ grouping provider {
+ description
+ "This grouping represents a VSG agent/provider which manages multiple
+ VSG gateways. The VSG agent provides agregate abstraction of
+ the entire NFaaS as a single switch to the controller. Each
+ port entry of the agent represents each VSG subscriber flow as
+ a separate openflow port.";
+
+ list gateway {
+ description
+ "Each entry represents a VSG instance managed by the agent.";
+
+ leaf id { type xos:unique-identifier; }
+
+ container services {
+ description
+ "Contains various services provided by the gateway.";
+ container cdn {
+ if-feature cdn;
+ }
+ container firewall {
+ if-feature firewall;
+ leaf-list rules { type string; }
+ }
+ container url-filter {
+ if-feature url-filter;
+ leaf level {
+ type enumeration {
+ enum "PG";
+ // others...
+ }
+ }
+ leaf-list rules { type string; }
+ }
+ container uverse {
+ if-feature uverse;
+ }
+ }
+ list flow {
+ description
+ "Each flow represents a subscriber flow into the VSG instance.";
+ leaf id {
+ type yang:uuid;
+ }
+ }
+ }
+ list port {
+ description
+ "Each entry represents a VSG subscriber flow connected across VSG
+ gateways.";
+ key id;
+ leaf id {
+ description "OpenFlow Port ID";
+ type yang:uuid;
+ mandatory true;
+ }
+ leaf link {
+ type leafref {
+ path '../../gateway/flow/id';
+ }
+ mandatory true;
+ }
+ }
+ }
+ grouping subscriber {
+ description
+ "This grouping represents a VSG service subscriber along with
+ reference to fabric flows used by the subscriber.";
+
+ leaf status {
+ type enumeration {
+ enum "enabled" {
+ description "Enabled";
+ value 1;
+ }
+ enum "suspended" {
+ description "Suspended";
+ }
+ enum "delinquent" {
+ description "Delinquent";
+ }
+ enum "violation" {
+ description "Copyright Violation";
+ }
+ }
+ default enabled;
+ }
+ leaf demo { type boolean; default false; }
+ leaf uplink-speed { type volt:bandwidth; }
+ leaf downlink-speed { type volt:bandwidth; }
+
+ list tag {
+ description
+ "Each entry represents a unique openflow port ID that the subscriber
+ connects to the VSG service from the fabric.";
+
+ key flow;
+ leaf flow {
+ type union {
+ type volt:subscriber-flow;
+ type vsg:subscriber-flow {
+ description "can be an output flow from the VSG service.";
+ }
+ type yang:uuid {
+ description "supports a generic openflow port ID";
+ }
+ }
+ }
+ }
+ leaf-list flows {
+ description
+ "Each entry represents a unique openflow port ID that the subscriber
+ uses to connect into the fabric from the VSG service.";
+ config false;
+ type subscriber-flow;
+ }
+ }
+
+ /*
+ * Configuration data
+ */
+ container service {
+ uses xos:service {
+ refine kind { default vsg-service; }
+ augment "provider" { uses vsg:provider; }
+ augment "subscriber" { uses vsg:subscriber; }
+ }
+ }
+}
diff --git a/schema/cord-subscriber.yang b/schema/cord-subscriber.yang
new file mode 100644
index 0000000..86fa0e8
--- /dev/null
+++ b/schema/cord-subscriber.yang
@@ -0,0 +1,148 @@
+module cord-subscriber {
+ namespace "urn:onlab:cord:subscriber";
+ prefix csub;
+ yang-version 1.1;
+
+ organization
+ "Open Networking Lab (CORD) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ description
+ "This module contains CORD Subscriber model representing its
+ relationship to features and services.
+
+ Copyright (c) 2016 ON.LAB 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 of the Apache License, Version 2.0 which accompanies this
+ distribution, and is available at
+ (http://www.apache.org/licenses/LICENSE-2.0).";
+
+ revision 2016-07-14 {
+ description "Initial revision.";
+ }
+
+ import xos-core { prefix xos; }
+ import cord-device { prefix dev; }
+ import ietf-yang-types { prefix yang; }
+ import corenova-node { prefix node; }
+
+ identity cord-subscriber { base xos:subscriber; }
+
+ grouping subscriber {
+ uses xos:subscriber {
+ refine kind { default cord-subscriber; }
+ }
+ leaf status {
+ type enumeration {
+ enum "enabled" {
+ description "Enabled";
+ value 1;
+ }
+ enum "suspended" {
+ description "Suspended";
+ }
+ enum "delinquent" {
+ description "Delinquent";
+ }
+ enum "violation" {
+ description "Copyright Violation";
+ }
+ }
+ default enabled;
+ }
+ leaf demo { type boolean; default false; }
+ leaf uplink-speed {
+ type dev:bandwidth;
+ default 1000000000;
+ }
+ leaf downlink-speed {
+ type dev:bandwidth;
+ default 1000000000;
+ }
+
+ container services {
+ description
+ "Contains various services available to the subscriber";
+
+ container cdn {
+ leaf enabled { type boolean; default false; }
+ }
+ container firewall {
+ leaf enabled { type boolean; default false; }
+ leaf-list rules {
+ // should qualify further
+ type string;
+ }
+ }
+ container url-filter {
+ leaf enabled { type boolean; default false; }
+ leaf level {
+ type enumeration {
+ enum "PG";
+ // other types of level...
+ }
+ }
+ leaf-list rules {
+ // should qualify further
+ type string;
+ }
+ }
+ container uverse {
+ leaf enabled { type boolean; default false; }
+ }
+ }
+
+ list device {
+ uses dev:device;
+ key "mac";
+
+ action create {
+ input { uses dev:device; }
+ }
+ action update {
+ input { uses dev:device; }
+ }
+ action delete;
+ }
+ }
+
+ grouping subscriber-controller {
+ uses subscriber;
+
+ node:view "features identity related";
+
+ container features {
+ node:link cdn { path "../services/cdn/enabled"; }
+ node:link uplink-speed { path "../uplink-speed"; }
+ node:link downlink-speed { path "../downlink-speed"; }
+ node:link uverse { path "../services/uverse/enabled"; }
+ node:link status { path "../status"; }
+
+ action update {
+ description "when invoked, updates the features container (PUT)";
+ }
+ }
+
+ container identity {
+ node:link account-num { path "../service-specific-id"; }
+ node:link name { path "../name"; }
+
+ action update {
+ description "when invoked, updates the identity container (PUT)";
+ }
+ }
+
+ container related {
+ config false;
+ description
+ "placeholder where other services can augment for info they want to
+ share (READ-ONLY)";
+ }
+ }
+}
diff --git a/schema/corenova-node.yang b/schema/corenova-node.yang
new file mode 100644
index 0000000..e0873e0
--- /dev/null
+++ b/schema/corenova-node.yang
@@ -0,0 +1,41 @@
+module corenova-node {
+ namespace "urn:corenova:node";
+ prefix node;
+
+ organization
+ "Corenova Technologies, Inc.";
+
+ contact
+ "Peter K. Lee <peter@corenova.com>";
+
+ description
+ "This module defines extensions to dynamically control schema node
+ display and access overlay controls";
+
+ feature corenova-node {
+ description
+ "this is to indicate the agent supports dynamic schema node
+ link and view constraints.";
+ }
+
+ extension link {
+ description
+ "Links a new 'link-name' element to an alternate data node element in
+ the schema data tree referenced by the 'path' sub statement. It
+ can be used within 'container' or 'list' type data element to
+ hold one or more such 'links' as part of its configuration data
+ tree. The new link element becomes a 'mirror' instance of the
+ target node found in the path expression.";
+ argument link-name {
+ yin-element true;
+ }
+ }
+
+ extension view {
+ description
+ "Informs the interface layers that only the data nodes referenced
+ within the value argument should be made visible to the
+ consumer.";
+ argument value;
+ }
+}
diff --git a/schema/experimental/xos-core-service.yang b/schema/experimental/xos-core-service.yang
new file mode 100644
index 0000000..3923221
--- /dev/null
+++ b/schema/experimental/xos-core-service.yang
@@ -0,0 +1,120 @@
+module xos-core-service {
+ namespace "urn:xos:core:service";
+ prefix xos-cs;
+
+ import complex-types { prefix ct; }
+
+ revision 2015-10-01 {
+ description "Initial revision.";
+ }
+
+ grouping service-attribute {
+ leaf name { type string { length 128; } }
+ leaf value { type string { length 1024; } }
+ leaf service {
+ type instance-identifier { ct:instance-type Service; require-instance true; }
+ }
+ }
+
+ grouping service-role {
+ leaf role {
+ type enumeration {
+ enum "admin";
+ //enum "Admin";
+ }
+ }
+ }
+
+ grouping common-model-attr {
+ leaf kind {
+ type string { length 30; }
+ default "generic";
+ }
+ leaf name { type string { length 255; } }
+ }
+
+ ct:complex-type ServiceElement {
+ ct:abstract true;
+ leaf enabled { type boolean; default true; }
+ }
+
+ ct:complex-type Service {
+ ct:extends ServiceElement;
+
+ leaf description {
+ type string { length 255; }
+ description "Description of Service";
+ }
+ leaf published { type boolean; default true; }
+
+ uses common-model-attr {
+ refine kind {
+ description "Kind of Service";
+ }
+ refine name {
+ description "Service Name";
+ }
+ }
+ }
+
+ ct:complex-type User {
+ ct:extends ServiceElement;
+ // TBD - should go to a separate xos-core-user module or such
+ }
+
+ ct:complex-type ServicePrivilege {
+ key "user-service-role";
+
+ leaf user {
+ type instance-identifier { ct:instance-type User; require-instance true; }
+ }
+ leaf service {
+ type instance-identifier { ct:instance-type Service; }
+ }
+ uses service-role;
+ }
+
+ ct:complex-type TenantRoot {
+ ct:extends ServiceElement;
+
+ description
+ "A tenantRoot is one of the things that can sit at the root of a chain
+ of tenancy. This object represents a node.";
+ uses common-model-attr;
+ }
+
+ ct:complex-type ContainerImage {
+ // TBD
+ }
+
+ ct:complex-type Tenancy {
+ ct:extends ServiceElement;
+
+ description
+ "A Tenancy describes relationship between a subscriber and a provider";
+
+ uses common-model-attr;
+
+ leaf provider { type instance-identifer { ct:instance-type Service; } }
+ leaf subscriber {
+ type instance-identifier {
+ ct:instance-type ServiceElement;
+ }
+ }
+
+ // adding stuff from TenantWithContainer here...
+ leaf creator { type instance-identifier { ct:instance-type User; } }
+ leaf image { type instance-identifier { ct:instance-type ContainerImage; } }
+ }
+
+ ct:complex-type Subscriber {
+ ct:extends TenantRoot;
+ refine kind { default "Subscriber"; }
+ }
+
+ ct:complex-type Provider {
+ ct:extends TenantRoot;
+ refine kind { default "Provider"; }
+ }
+
+}
diff --git a/schema/xos-core.yang b/schema/xos-core.yang
new file mode 100644
index 0000000..d1ad954
--- /dev/null
+++ b/schema/xos-core.yang
@@ -0,0 +1,1350 @@
+module xos-core {
+ namespace "urn:onlab:xtype:core";
+ prefix xos;
+ yang-version 1.1;
+
+ import ietf-yang-types { prefix yang; }
+ import ietf-inet-types { prefix inet; }
+ import xos-types { prefix xtype; }
+
+ organization
+ "Open Networking Lab (XOS) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ description
+ "This module contains a collection of core models for XOS.
+
+ Copyright (c) 2016 ON.LAB 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 of the Apache License, Version 2.0 which accompanies this
+ distribution, and is available at
+ (http://www.apache.org/licenses/LICENSE-2.0).";
+
+ revision 2016-07-14 {
+ description "Initial revision.";
+ }
+
+ feature synchronizer {
+ description
+ "Enables configuration synchronization to the distributed store.";
+ }
+
+ identity kind;
+ identity generic { base kind; }
+ identity service { base kind; }
+
+ grouping attribute-pair {
+ leaf name { type string { length 0..128; } }
+ leaf value { type string; }
+ // don't need pointer back to service
+ }
+
+ grouping sync-record {
+ description "Synchronizer-specific properties for model records";
+
+ leaf created { type yang:date-and-time; }
+ leaf updated { type yang:date-and-time; }
+ leaf enacted { type yang:date-and-time; }
+ leaf policed { type yang:date-and-time; }
+
+ leaf writable { type boolean; default true; }
+ leaf locked { type boolean; default false; }
+ leaf deleted { type boolean; default false; }
+
+ leaf dirty {
+ config false;
+ type boolean;
+ default false;
+ }
+
+ container sync {
+ anydata register {
+ description "scratchpad used by the Observer";
+ }
+ leaf progress {
+ type enumeration {
+ enum provisioning {
+ value 0;
+ description "Provisioning in progress";
+ }
+ }
+ }
+ leaf disabled { type boolean; default false; }
+ leaf enforced { type boolean; default true; }
+
+ list policy {
+ // TODO: how are policy defined/enforced?
+ }
+ }
+
+ action diff {
+ when "../dirty == true";
+ description "retrieve diff of model state if dirty";
+ }
+ action save {
+ description "trigger save into data store via synchronizer";
+ }
+ }
+
+ grouping base-common {
+ leaf id {
+ type xtype:unique-identifier;
+ mandatory true;
+ }
+ leaf name {
+ type string {
+ length 0..255;
+ }
+ }
+ list attribute {
+ uses attribute-pair;
+ key name;
+ status deprecated;
+ reference "XOS: service-specific-attribute";
+ description "backwards-compatible attribute association";
+ }
+ leaf service-specific-id {
+ type xtype:unique-identifier;
+ mandatory true;
+ status deprecated;
+ }
+ container record {
+ if-feature synchronizer;
+ uses sync-record;
+ }
+ }
+ grouping tenant-root {
+ uses base-common {
+ refine 'name' {
+ description "Specify name of the TenantRoot";
+ }
+ }
+ description
+ "A Tenant Root is one of the things that can sit at the root of a chain
+ of tenancy. This object represents a node.";
+
+ list subscribed-tenant {
+ config false;
+ // not exactly clear how this is populated
+ }
+ }
+ grouping subscriber {
+ uses tenant-root {
+ refine kind { default subscriber; }
+ }
+ // seems we should have interesting attributes specific to subscriber?
+ }
+ grouping provider {
+ uses tenant-root {
+ refine kind { default provider; }
+ }
+ // seems we should have interesting attributes specific to provider?
+ }
+ grouping service {
+ uses base-common {
+ refine 'name' {
+ description "Name of the Service";
+ }
+ }
+ leaf kind {
+ type identityref {
+ base kind;
+ }
+ default generic;
+ }
+ leaf description {
+ type string {
+ length 0..254;
+ }
+ description "Description of the Service";
+ }
+ leaf version {
+ type string { length 0..30; }
+ description "Version of Service Definition";
+ }
+
+ leaf enabled { type boolean; default true; }
+ leaf published { type boolean; default true; }
+
+ container links {
+ leaf view { type inet:uri; }
+ leaf icon { type inet:uri; }
+ }
+
+ list keypair {
+ description "collection of public/private key pair(s)";
+ // should be a specific typedef for storing this content
+ leaf public { type string { length 0..1024; } }
+ leaf private { type string { length 0..1024; } }
+ }
+ list provider {
+ description
+ "Each entry represents a provider of the service. Each unique service
+ should augment this block with service specific attributes.";
+ key id;
+ uses xtype:provider;
+ }
+ list subscriber {
+ description
+ "Each entry represents a subscriber of the service. Each unique service
+ should augment this block with service specific attributes.";
+ key id;
+ uses xtype:subscriber;
+ notification subscriber-added;
+ notification subscriber-deleted;
+ }
+ list slice {
+ uses xtype:slice;
+ }
+
+ // TOOD: need to better understand relationship between Service and Slice
+ action scale {
+ description "Adjust scale for slice(s)";
+ }
+
+ // TODO: need to better understand relationship between Service and VTN
+ }
+
+ grouping tenancy {
+ uses base-common;
+
+ choice provider {
+ description "only one type of provider node is valid.";
+ case service { leaf provider-service { type instance-identifier; } }
+ }
+
+ choice subscriber {
+ description "only one type of subscriber node is valid.";
+ case service { leaf subscriber-service { type instance-identifier; } }
+ case tenant { leaf subscriber-tenant { type instance-identifier; } }
+ case user { leaf subscriber-user { type instance-identifier; } }
+ case root { leaf subscriber-root { type instance-identifier; } }
+ case network { leaf subscriber-network { type instance-identifier; } }
+ }
+
+ leaf connect-method {
+ //when "../kind == 'static-tenant'";
+
+ type enumeration {
+ enum public { description "Public"; }
+ enum private { description "Private"; }
+ enum private-unidirectional { description "Private Uni-directional"; }
+ enum na { description "Not Applicable"; }
+ }
+ default na;
+ }
+
+ // TODO: should be able to deal with TenantWithContainer here
+
+ }
+
+ grouping slice {
+ description
+ "A slice is a logically centralized container for network and compute resources"
+
+ uses base-common;
+
+ leaf enabled {
+ type boolean;
+ default true;
+ }
+
+ leaf omf-friendly {
+ type boolean;
+ default false;
+ status deprecated;
+ }
+
+ leaf slice-url {
+ description "A URL describing the purpose of this slice";
+ type xtype:url-field;
+ // blank true;
+ }
+
+ leaf max-instances {
+ description "The maximum number of VMs that this slice is allowed to allocate";
+ type uint32;
+ }
+
+ leaf service {
+ description "The service that runs in this slice";
+ type xtype:service;
+ }
+
+ leaf network {
+ description "The network that the slice uses to connect to other slices and to the Internet.";
+ type string;
+ }
+
+ leaf exposed-ports {
+ description "The ports to be exposed for other slices to connect to";
+ type string;
+ }
+
+ leaf service-class {
+ type xtype:service-class;
+ status deprecated;
+ }
+
+ leaf creator {
+ type xtype:user;
+ }
+
+ leaf default-flavor {
+ type xtype:flavor;
+ }
+
+ leaf default-image {
+ type xtype:image;
+ }
+
+ leaf default-node {
+ type xtype:node;
+ }
+
+ leaf mount-data-sets {
+ type string {
+ length 0..256;
+ }
+ default "GenBank";
+ }
+
+ leaf default_isolation {
+ type string {
+ length 0..30;
+ }
+ default "vm";
+ }
+
+ leaf-list tags {
+ // below leafref is not valid since there is no /tag
+ type leafref {
+ path "/tag[id = current()/../id]/id";
+ }
+ }
+ }
+
+
+ grouping controller-images {
+ uses xos-base;
+
+ leaf image {
+ type xtype:image;
+ }
+
+ leaf controller {
+ type xtype:controller;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+ leaf glance-image-id {
+ type string;
+ }
+ }
+ }
+ }
+
+ grouping controller-site-privilege {
+ uses xos-base;
+
+ leaf controller {
+ type xtype:controller;
+ }
+
+ leaf site-privilege {
+ type xtype:site-privilege;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+
+ leaf role-id {
+ type string;
+ }
+ }
+ }
+
+ grouping image {
+ uses xos-base;
+
+
+ leaf kind {
+ type string;
+ }
+ leaf disk-format {
+ type string;
+ }
+ leaf container-format {
+ type string;
+ }
+ leaf path {
+ type string;
+ }
+ leaf tag {
+ type string;
+ }
+ }
+
+ grouping controller-network {
+ uses xos-base;
+ leaf network {
+ type xtype:network;
+ }
+ leaf controller {
+ type xtype:controller;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+ leaf net-id {
+ type string;
+ }
+ leaf router-id {
+ type string;
+ }
+ leaf subnet-id {
+ type string;
+ }
+ }
+
+ leaf subnet {
+ type string;
+ }
+
+ }
+
+ grouping site {
+ uses xos-base;
+
+ leaf site-url {
+ type xtype:url-field;
+ }
+ leaf enabled {
+ type boolean;
+ }
+ leaf hosts-nodes {
+ type boolean;
+ }
+ leaf hosts-users {
+ type boolean;
+ }
+ leaf location {
+ type xtype:geoposition-field;
+ }
+ leaf longitude {
+ type decimal64;
+ }
+ leaf latitude {
+ type decimal64;
+ }
+ leaf login-base {
+ type string;
+ }
+ leaf is-public {
+ type boolean;
+ }
+ leaf abbreviated-name {
+ type string;
+ }
+ }
+
+ grouping slice-role {
+ uses xos-base;
+ leaf role {
+ type string;
+ }
+ }
+
+ grouping site-deployment {
+ uses xos-base;
+ leaf site {
+ type xtype:site;
+ }
+ leaf deployment {
+ type xtype:deployment;
+ }
+ leaf controller {
+ type xtype:controller;
+ }
+ container synchronizer {
+ if-feature synchronizer {
+
+ leaf availability-zone {
+ type string;
+ }
+ }
+ }
+ }
+
+
+ grouping user-credential {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+
+ leaf key-id {
+ type string;
+ }
+
+ leaf enc-value {
+ type xtype:encrypted-string;
+ }
+ }
+
+ grouping invoice {
+ uses xos-base;
+ leaf date {
+ type xtype:datetime;
+ }
+ leaf account {
+ type xtype:account;
+ }
+ }
+
+ grouping slice-privilege {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+ leaf slice {
+ type xtype:slice;
+ }
+ leaf role {
+ type xtype:role;
+ }
+ }
+
+ grouping flavor {
+ uses xos-base;
+
+ leaf description {
+ type string;
+ }
+ leaf flavor {
+ type string;
+ }
+ leaf order {
+ type uint32;
+ }
+ leaf default {
+ type boolean;
+ }
+ }
+
+ grouping port {
+ uses xos-base;
+ leaf network {
+ type xtype:network;
+ }
+ leaf instance {
+ type xtype:instance;
+ }
+ container synchronizer {
+ if-feature synchronizer {
+ leaf ip {
+ type inet:ip-address;
+ }
+ leaf port-id {
+ type string;
+ }
+ leaf mac {
+ type string;
+ }
+ }
+ }
+ leaf xos-created {
+ type boolean;
+ }
+ }
+
+ grouping service-role {
+ uses xos-base;
+ leaf role {
+ type string;
+ }
+ }
+
+ grouping controller-site {
+ uses xos-base;
+ leaf site {
+ type xtype:site;
+ }
+
+ leaf controller {
+ type xtype:controller;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+
+ leaf tenant-id {
+ type string;
+ }
+ }
+ }
+ }
+
+ grouping controller-slice {
+ uses xos-base;
+ leaf controller {
+ type xtype:controller;
+ }
+ leaf slice {
+ type xtype:slice;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+ leaf tenant-id {
+ type string;
+ }
+ }
+ }
+ }
+
+ grouping tenant-role {
+ uses xos-base;
+ leaf role {
+ type string;
+ }
+ }
+
+ grouping network {
+ uses xos-base;
+ leaf template {
+ type xtype:template;
+ }
+
+ leaf subnet {
+ type string;
+ }
+
+ leaf ports {
+ type string;
+ }
+
+ leaf labels {
+ type string;
+ }
+
+ leaf owner {
+ type xtype:owner;
+ }
+
+ leaf guaranteed-bandwidth {
+ type uint32;
+ }
+
+ leaf permit-all-slices {
+ type boolean;
+ }
+
+ leaf topology-parameters {
+ type string;
+ }
+
+ leaf controller-url {
+ type string;
+ }
+
+ leaf controller-parameters {
+ type string;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+ leaf network-id {
+ type string;
+ }
+ leaf router-id {
+ type string;
+ }
+ leaf subnet-id {
+ type string;
+ }
+ }
+ }
+ leaf autoconnect {
+ type boolean;
+ }
+ }
+
+ grouping controller-role {
+ uses xos-base;
+ leaf role {
+ type string;
+ }
+ }
+
+ grouping diag {
+ uses xos-base;
+
+ }
+
+ grouping service-class {
+ uses xos-base;
+
+ leaf description {
+ type string;
+ }
+ leaf commitment {
+ type uint32;
+ }
+ leaf membership-fee {
+ type uint32;
+ }
+ leaf membership-fee-months {
+ type uint32;
+ }
+ leaf upgrade-requires-approval {
+ type boolean;
+ }
+ }
+
+ grouping site-role {
+ uses xos-base;
+ leaf role {
+ type string;
+ }
+ }
+
+ grouping instance {
+ uses xos-base;
+ container synchronizer {
+ if-feature synchronizer {
+ leaf instance-id {
+ type string;
+ }
+
+ leaf instance-uuid {
+ type string;
+ }
+
+ leaf instance-name {
+ type string;
+ }
+
+ leaf ip {
+ type inet:ip-address;
+ }
+ }
+ leaf image {
+ type xtype:image;
+ }
+ leaf creator {
+ type xtype:creator;
+ }
+ leaf slice {
+ type xtype:slice;
+ }
+ leaf deployment {
+ type xtype:deployment;
+ }
+ leaf node {
+ type xtype:node;
+ }
+ leaf number-cores {
+ type uint32;
+ }
+ leaf flavor {
+ type xtype:flavor;
+ }
+ leaf user-data {
+ type string;
+ }
+ leaf isolation {
+ type string;
+ }
+ leaf volumes {
+ type string;
+ }
+ leaf parent {
+ type xtype:parent;
+ }
+ }
+
+ grouping charge {
+ uses xos-base;
+ leaf account {
+ type xtype:account;
+ }
+ leaf slice {
+ type xtype:slice;
+ }
+ leaf kind {
+ type string;
+ }
+ leaf state {
+ type string;
+ }
+ leaf date {
+ type xtype:datetime;
+ }
+ leaf object {
+ type xtype:object;
+ }
+ leaf amount {
+ type decimal64;
+ }
+ leaf core-hours {
+ type decimal64;
+ }
+ leaf invoice {
+ type xtype:invoice;
+ }
+ }
+
+ grouping program {
+ uses xos-base;
+ leaf description {
+ type string;
+ }
+
+ leaf kind {
+ type string;
+ }
+
+ leaf command {
+ type string;
+ }
+
+ leaf owner {
+ type xtype:owner;
+ }
+
+ leaf contents {
+ type string;
+ }
+
+ leaf output {
+ type string;
+ }
+
+ leaf messages {
+ type string;
+ }
+
+ leaf status {
+ type string;
+ }
+ }
+
+ grouping role {
+ uses xos-base;
+ leaf role-type {
+ type string;
+ }
+ leaf role {
+ type string;
+ }
+ leaf description {
+ type string;
+ }
+ leaf content-type {
+ type xtype:content-type;
+ }
+ }
+
+ grouping usable-object {
+ uses xos-base;
+ }
+
+ grouping node-label {
+ uses xos-base;
+ }
+
+ grouping slice-credential {
+ uses xos-base;
+ leaf slice {
+ type xtype:slice;
+ }
+
+ leaf key-id {
+ type string;
+ }
+ leaf enc-value {
+ type xtype:encrypted-string;
+ }
+ }
+
+ grouping node {
+ uses xos-base;
+ leaf site-deployment {
+ type xtype:site-deployment;
+ }
+ leaf site {
+ type xtype:site;
+ }
+ }
+
+ grouping address-pool {
+ uses xos-base;
+ leaf addresses {
+ type string;
+ }
+ leaf gateway-ip {
+ type string;
+ }
+ leaf gateway-mac {
+ type string;
+ }
+ leaf cidr {
+ type string;
+ }
+ leaf inuse {
+ type string;
+ }
+ leaf service {
+ type xtype:service;
+ }
+ }
+
+ grouping dashboard-view {
+ uses xos-base;
+ leaf url {
+ type string;
+ }
+ leaf enabled {
+ type boolean;
+ }
+ }
+
+ grouping network-parameter {
+ uses xos-base;
+ leaf parameter {
+ type xtype:parameter;
+ }
+ leaf value {
+ type string;
+ }
+ leaf content-type {
+ type xtype:content-type;
+ }
+ leaf object-id {
+ type uint32;
+ }
+ }
+
+ grouping image-deployments {
+ uses xos-base;
+ leaf image {
+ type xtype:image;
+ }
+ leaf deployment {
+ type xtype:deployment;
+ }
+ }
+
+ grouping controller-user {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+
+ leaf controller {
+ type xtype:controller;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+ leaf kuser-id {
+ type string;
+ }
+ }
+ }
+ }
+
+ grouping reserved-resource {
+ uses xos-base;
+ leaf instance {
+ type xtype:instance;
+ }
+ leaf resource {
+ type xtype:resource;
+ }
+ leaf quantity {
+ type uint32;
+ }
+ leaf reservationSet {
+ type xtype:reservationSet;
+ }
+ }
+
+ grouping network-template {
+ uses xos-base;
+ leaf description {
+ type string;
+ }
+ leaf guaranteed-bandwidth {
+ type uint32;
+ }
+ leaf visibility {
+ type string;
+ }
+ leaf translation {
+ type string;
+ }
+ leaf access {
+ type string;
+ }
+ leaf shared-network-name {
+ type string;
+ }
+ leaf shared-network-id {
+ type string;
+ }
+ leaf topology-kind {
+ type string;
+ }
+ leaf controller-kind {
+ type string;
+ }
+ }
+
+ grouping controller-dashboard-view {
+ uses xos-base;
+ leaf controller {
+ type xtype:controller;
+ }
+ leaf dashboardView {
+ type xtype:dashboardView;
+ }
+ leaf enabled {
+ type boolean;
+ }
+ leaf url {
+ type string;
+ }
+ }
+
+ grouping user-dashboard-view {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+ leaf dashboardView {
+ type xtype:dashboardView;
+ }
+ leaf order {
+ type uint32;
+ }
+ }
+
+ grouping controller {
+ uses xos-base;
+ leaf backend-type {
+ type string;
+ }
+ leaf version {
+ type string;
+ }
+ leaf auth-url {
+ type string;
+ }
+ leaf admin-user {
+ type string;
+ }
+ leaf admin-password {
+ type string;
+ }
+ leaf admin-tenant {
+ type string;
+ }
+ leaf domain {
+ type string;
+ }
+ leaf rabbit-host {
+ type string;
+ }
+ leaf rabbit-user {
+ type string;
+ }
+ leaf rabbit-password {
+ type string;
+ }
+ leaf deployment {
+ type xtype:deployment;
+ }
+ }
+
+ grouping user {
+ uses xos-base;
+ leaf password {
+ type string;
+ }
+ leaf last-login {
+ type xtype:datetime;
+ }
+ leaf email {
+ type EmailField;
+ }
+ leaf username {
+ type string;
+ }
+ leaf firstname {
+ type string;
+ }
+ leaf lastname {
+ type string;
+ }
+ leaf phone {
+ type string;
+ }
+ leaf user-url {
+ type xtype:url-field;
+ }
+ leaf site {
+ type xtype:site;
+ }
+ leaf public-key {
+ type string;
+ }
+ leaf is-active {
+ type boolean;
+ }
+ leaf is-admin {
+ type boolean;
+ }
+ leaf is-staff {
+ type boolean;
+ }
+ leaf is-readonly {
+ type boolean;
+ }
+ leaf is-registering {
+ type boolean;
+ }
+ leaf is-appuser {
+ type boolean;
+ }
+ leaf login-page {
+ type string;
+ }
+ leaf timezone {
+ type xtype:time-zone-field;
+ }
+ }
+
+ grouping deployment {
+ uses xos-base;
+
+ leaf access-control {
+ type string;
+ }
+ }
+
+ grouping reservation {
+ uses xos-base;
+ leaf start-time {
+ type xtype:datetime;
+ }
+ leaf slice {
+ type xtype:slice;
+ }
+ leaf duration {
+ type uint32;
+ }
+ }
+
+ grouping site-privilege {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+ leaf site {
+ type xtype:site;
+ }
+ leaf role {
+ type xtype:role;
+ }
+ }
+
+ grouping payment {
+ uses xos-base;
+ leaf account {
+ type xtype:account;
+ }
+ leaf amount {
+ type decimal64;
+ }
+ leaf date {
+ type xtype:datetime;
+ }
+ }
+
+ grouping network-slice {
+ uses xos-base;
+ leaf network {
+ type xtype:network;
+ }
+
+ leaf slice {
+ type xtype:slice;
+ }
+ }
+
+ grouping account {
+ uses xos-base;
+ leaf site {
+ type xtype:site;
+ }
+ }
+
+ grouping controller-slice-privilege {
+ uses xos-base;
+ leaf controller {
+ type xtype:controller;
+ }
+
+ leaf slice-privilege {
+ type xtype:slice-privilege;
+ }
+
+ container synchronizer {
+ if-feature synchronizer {
+ leaf role-id {
+ type string;
+ }
+ }
+ }
+ }
+
+ grouping site-credential {
+ uses xos-base;
+ leaf site {
+ type xtype:site;
+ }
+
+ leaf key-id {
+ type string;
+ }
+
+ leaf enc-value {
+ type xtype:encrypted-string;
+ }
+ }
+
+ grouping deployment-privilege {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+
+ leaf deployment {
+ type xtype:deployment;
+ }
+
+ leaf role {
+ type xtype:role;
+ }
+ }
+
+ grouping network-parameter-type {
+ uses xos-base;
+ leaf description {
+ type string;
+ }
+ }
+
+ grouping deployment-role {
+ uses xos-base;
+ leaf role {
+ type string;
+ }
+ }
+
+ grouping project {
+ uses xos-base;
+ }
+
+ grouping slice-tag {
+ uses xos-base;
+ leaf slice {
+ type xtype:slice;
+ }
+
+ leaf value {
+ type string;
+ }
+ }
+
+ grouping router {
+ uses xos-base;
+ leaf owner {
+ type xtype:owner;
+ }
+ }
+
+ grouping service-resource {
+ uses xos-base;
+ leaf service-class {
+ type xtype:service-class;
+ }
+
+ leaf max-units-deployment {
+ type uint32;
+ }
+
+ leaf max-units-node {
+ type uint32;
+ }
+
+ leaf max-duration {
+ type uint32;
+ }
+
+ leaf bucket-in-rate {
+ type uint32;
+ }
+
+ leaf bucket-max-size {
+ type uint32;
+ }
+
+ leaf cost {
+ type uint32;
+ }
+
+ leaf calendar-reservable {
+ type boolean;
+ }
+ }
+
+ grouping service-privilege {
+ uses xos-base;
+ leaf user {
+ type xtype:user;
+ }
+ leaf service {
+ type xtype:service;
+ }
+ leaf role {
+ type xtype:role;
+ }
+ }
+
+ /*** main configuration tree for XOS ***/
+
+ container api {
+ description
+ "The primary configuration interaction endpoint";
+
+ container service {
+ description
+ "placeholder endpoint for services to augment";
+ }
+ container tenant {
+ description
+ "placeholder endpoint for tenants to augment";
+ }
+ }
+
+}
diff --git a/schema/xos-types.yang b/schema/xos-types.yang
new file mode 100644
index 0000000..da58981
--- /dev/null
+++ b/schema/xos-types.yang
@@ -0,0 +1,240 @@
+module xos-types {
+ namespace "urn:onlab:xos:types";
+ prefix xtype;
+ yang-version 1.1;
+
+ organization
+ "Open Networking Lab (CORD) / Corenova Technologies";
+
+ contact
+ "Larry Peterson <llp@onlab.us>
+ Peter K. Lee <peter@corenova.com>";
+
+ import ietf-yang-types { prefix yang; }
+
+ revision 2016-09-12 {
+ description "Initial revision.";
+ }
+
+ /*
+ * Typedefs
+ */
+ typedef unique-identifier {
+ description "defines valid formats for external reference id";
+ type union {
+ type uint32 { range 1..max; }
+ type yang:uuid;
+ type inet:uri;
+ }
+ }
+ typedef bandwidth {
+ type uint32 {
+ range 1000000..max; // should be at least 1 Mbps?
+ }
+ units 'bps';
+ }
+ typedef vlan {
+ type uint16 { range 0..4095; }
+ }
+ typedef image {
+ type unique-identifier;
+ }
+ typedef controller-network {
+ type unique-identifier;
+ }
+ typedef site {
+ type unique-identifier;
+ }
+ typedef tenant-root-role {
+ type unique-identifier;
+ }
+ typedef slice-role {
+ type unique-identifier;
+ }
+ typedef site-deployment {
+ type unique-identifier;
+ }
+ typedef tenant-privilege {
+ type unique-identifier;
+ }
+ typedef tag {
+ type unique-identifier;
+ }
+ typedef user-credential {
+ type unique-identifier;
+ }
+ typedef invoice {
+ type unique-identifier;
+ }
+ typedef slice-privilege {
+ type unique-identifier;
+ }
+ typedef flavor {
+ type unique-identifier;
+ }
+ typedef port {
+ type unique-identifier;
+ }
+ typedef service-role {
+ type unique-identifier;
+ }
+ typedef controller-site {
+ type unique-identifier;
+ }
+ typedef controller-slice {
+ type unique-identifier;
+ }
+ typedef tenant-role {
+ type unique-identifier;
+ }
+ typedef slice {
+ type unique-identifier;
+ }
+ typedef network {
+ type unique-identifier;
+ }
+ typedef controller-role {
+ type unique-identifier;
+ }
+ typedef diag {
+ type unique-identifier;
+ }
+ typedef service-class {
+ type unique-identifier;
+ }
+ typedef tenant-attribute {
+ type unique-identifier;
+ }
+ typedef site-role {
+ type unique-identifier;
+ }
+ typedef subscriber {
+ type unique-identifier;
+ }
+ typedef instance {
+ type unique-identifier;
+ }
+ typedef charge {
+ type unique-identifier;
+ }
+ typedef program {
+ type unique-identifier;
+ }
+ typedef role {
+ type unique-identifier;
+ }
+ typedef usable-object {
+ type unique-identifier;
+ }
+ typedef node-label {
+ type unique-identifier;
+ }
+ typedef slice-credential {
+ type unique-identifier;
+ }
+ typedef node {
+ type unique-identifier;
+ }
+ typedef address-pool {
+ type unique-identifier;
+ }
+ typedef dashboard-view {
+ type unique-identifier;
+ }
+ typedef network-parameter {
+ type unique-identifier;
+ }
+ typedef image-deployments {
+ type unique-identifier;
+ }
+ typedef controller-user {
+ type unique-identifier;
+ }
+ typedef reserved-resource {
+ type unique-identifier;
+ }
+ typedef network-template {
+ type unique-identifier;
+ }
+ typedef controller-dashboard-view {
+ type unique-identifier;
+ }
+ typedef user-dashboard-view {
+ type unique-identifier;
+ }
+ typedef controller {
+ type unique-identifier;
+ }
+ typedef user {
+ type unique-identifier;
+ }
+ typedef deployment {
+ type unique-identifier;
+ }
+ typedef reservation {
+ type unique-identifier;
+ }
+ typedef site-privilege {
+ type unique-identifier;
+ }
+ typedef payment {
+ type unique-identifier;
+ }
+ typedef tenant {
+ type unique-identifier;
+ }
+ typedef network-slice {
+ type unique-identifier;
+ }
+ typedef account {
+ type unique-identifier;
+ }
+ typedef tenant-root {
+ type unique-identifier;
+ }
+ typedef service {
+ type unique-identifier;
+ }
+ typedef controller-slice-privilege {
+ type unique-identifier;
+ }
+ typedef site-credential {
+ type unique-identifier;
+ }
+ typedef deployment-privilege {
+ type unique-identifier;
+ }
+ typedef network-parameter-type {
+ type unique-identifier;
+ }
+ typedef provider {
+ type unique-identifier;
+ }
+ typedef tenant-with-container {
+ type unique-identifier;
+ }
+ typedef deployment-role {
+ type unique-identifier;
+ }
+ typedef project {
+ type unique-identifier;
+ }
+ typedef tenant-root-privilege {
+ type unique-identifier;
+ }
+ typedef slice-tag {
+ type unique-identifier;
+ }
+ typedef coarse-tenant {
+ type unique-identifier;
+ }
+ typedef router {
+ type unique-identifier;
+ }
+ typedef service-resource {
+ type unique-identifier;
+ }
+ typedef service-privilege {
+ type unique-identifier;
+ }
+}
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..3858028
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,443 @@
+# API Guide
+
+This document provides a comprehensive overview of the REST API
+interactions enabled via the
+[yang-express](http://github.com/corenova/yang-express) middleware
+framework built on [Express.js](http://expressjs.com).
+
+While much care has been taken to auto-generate the API endpoint
+routing to conform as closely as possible to
+[RESTCONF](https://datatracker.ietf.org/doc/draft-ietf-netconf-restconf),
+there are many key deviations and currently no plans to adapt the
+default REST API generator to be RESTCONF compatible.
+
+Instead, the REST API generated by the YANG data models are much more
+closely aligned to conventional REST APIs developed for use in the **web
+application development** community. Also, there's currently no support
+for XML but instead follows the
+[YANG-JSON](https://datatracker.ietf.org/doc/draft-ietf-netmod-yang-json)
+specifications for representing configuration data in JSON.
+
+Below is the primary snippet from [server.coffee](./server.coffee)
+demonstrating how to use
+[yang-express](http://github.com/corenova/yang-express) middleware
+framework to fire up a web server instance capable of serving up the
+YANG model-driven CORD reference implementation.
+
+```coffeescript
+yang = require('yang-js')
+app = require('yang-express') ->
+ @set 'pkginfo', require('../../package.json')
+ @set 'initial state', require('../../sample-data.json')
+ @enable 'openapi', 'restjson', 'websocket'
+
+ cord = @link yang.require('cord-core')
+ xos = @link yang.require('xos-core')
+
+ cord.on 'update', (prop) ->
+ console.log "[#{prop.path}] got updated, should consider persisting the change somewhere"
+```
+
+Yep, it's really that simple.
+
+## CORD API Reference
+
+The following section presents various CRUD operations that are
+*runtime generated* from the `cord-core` YANG module. These operations
+are essentially interpreted and served **during runtime** depending on
+the YANG runtime model along with the configuration data state at any given
+point in time. Basically, it performs *adaptive* API routing and
+rendering.
+
+### Open-API Specification (Swagger 2.0)
+
+Initial partial-support for dynamic **openapi** specification
+generation has been introduced with the latest
+[yang-express](http://github.com/corenova/yang-express) middleware
+framework. It can send back JSON or YAML based specification output
+depending on the requesting client's `Accept` header. It defaults to
+JSON output.
+
+You can give it a try by issuing one of the following:
+```bash
+$ curl localhost:5050/swagger.spec
+$ curl localhost:5050/swagger.spec -H 'accept: text/yaml'
+```
+
+We'll plan to fully support the **openapi** specification along the way.
+
+### View Subscribers
+
+Valid URIs:
+
+- GET /cord-core:subscriber
+- GET /cord:subscriber
+- GET /subscriber
+
+Request:
+```bash
+$ curl localhost:5050/cord:subscriber
+```
+Response:
+```json
+{
+ "cord-core:subscriber": [
+ {
+ "id": 1,
+ "service-specific-id": 1000,
+ "humanReadableName": "cordSubscriber-1",
+ "status": "enabled",
+ "demo": false,
+ "uplink-speed": 1000000000,
+ "downlink-speed": 1000000000,
+ "kind": "cord-subscriber",
+ "related": {}
+ },
+ {
+ "id": 2,
+ "service-specific-id": 1001,
+ "humanReadableName": "cordSubscriber-2",
+ "status": "enabled",
+ "demo": false,
+ "uplink-speed": 1000000000,
+ "downlink-speed": 1000000000,
+ "kind": "cord-subscriber",
+ "related": {}
+ }
+ ]
+}
+```
+
+### View Details for a Subscriber
+
+Valid URIs:
+
+- GET /cord-core:subscriber/:id
+- GET /cord:subscriber/:id
+- GET /subscriber/:id
+
+Request:
+```bash
+$ curl localhost:5050/cord:subscriber/1
+```
+Response:
+```json
+{
+ "cord-core:subscriber": {
+ "id": 1,
+ "service-specific-id": 1000,
+ "humanReadableName": "cordSubscriber-1",
+ "status": "enabled",
+ "demo": false,
+ "uplink-speed": 1000000000,
+ "downlink-speed": 1000000000,
+ "kind": "cord-subscriber",
+ "related": {}
+ }
+}
+```
+
+### Update a Subscriber
+
+Valid URIs:
+
+- PUT /cord-core:subscriber/:id
+- PUT /cord:subscriber/:id
+- PUT /subscriber/:id
+
+Request:
+```bash
+$ curl -X PUT localhost:5050/cord:subscriber/1 -H 'content-type: application/json' -d '{ "id": 10, "status": "suspended", "services": { "cdn": { "enabled": true } } }'
+```
+Response:
+```json
+{
+ "cord-core:subscriber": {
+ "id": 10,
+ "service-specific-id": 1000,
+ "humanReadableName": "cordSubscriber-10",
+ "status": "suspended",
+ "demo": false,
+ "uplink-speed": 1000000000,
+ "downlink-speed": 1000000000,
+ "kind": "cord-subscriber",
+ "related": {},
+ "services": {
+ "cdn": {
+ "enabled": true
+ }
+ }
+ }
+}
+```
+
+This operation highlights an interesting scenario related to
+*invisible* YANG schema properties. Any configuration tree segment
+that does not contain any data behave as *shadow* properties, which
+basically means they spring to life once valid data gets placed into
+it but stays *invisible* otherwise.
+
+### Delete a Subscriber
+
+Valid URIs:
+
+- DELETE /cord-core:subscriber/:id
+- DELETE /cord:subscriber/:id
+- DELETE /subscriber/:id
+
+Request:
+```bash
+$ curl -X DELETE localhost:5050/cord:subscriber/2
+```
+Response:
+```
+HTTP 204
+```
+
+### Create a new Subscriber
+
+Valid URIs:
+
+- POST /cord-core:subscriber
+- POST /cord:subscriber
+- POST /subscriber
+
+Request:
+```bash
+$ curl -X POST localhost:5050/cord:subscriber -H 'content-type: application/json' -d '{ "id": 12, "service-specific-id": 2020, "demo": true }'
+```
+Response:
+```json
+{
+ "cord-core:subscriber": [
+ {
+ "id": 12,
+ "service-specific-id": 2020,
+ "demo": true,
+ "humanReadableName": "cordSubscriber-12",
+ "status": "enabled",
+ "uplink-speed": 1000000000,
+ "downlink-speed": 1000000000,
+ "kind": "cord-subscriber",
+ "related": {}
+ }
+ ]
+}
+```
+The POST operation is only available on YANG `list` schema elements as
+well as `rpc` and `action` elements. This operation also accepts
+**bulk create** request data by supplying a JSON array.
+
+### Describe the Subscriber Model
+
+This operation provides discovery of a given data model's
+characteristics as well as other data node elements available inside
+the data model. It can be used by REST API consumers to auto-discover
+additional paths and metadata associated with the target entity. It's
+also useful for *dynamically generating API documentation* for a given
+data model.
+
+Valid URIs:
+
+- OPTIONS /cord-core:subscriber
+- OPTIONS /cord:subscriber
+- OPTIONS /subscriber
+
+Request:
+```bash
+$ curl -X OPTIONS localhost:5050/cord:subscriber
+```
+Response:
+```json
+{
+ "name": "cord-core:subscriber",
+ "kind": "list",
+ "path": "/cord-core:subscriber",
+ "exists": true,
+ "key": "id",
+ "description": "Authorative list of all subscriber instances",
+ "data": {
+ "humanReadableName": {
+ "kind": "leaf",
+ "config": false,
+ "type": {
+ "string": {
+ "pattern": "/^cordSubscriber-\\w+$/"
+ }
+ }
+ },
+ "status": {
+ "kind": "leaf",
+ "type": {
+ "enumeration": {
+ "enum": {
+ "enabled": {
+ "description": "Enabled",
+ "value": "1"
+ },
+ "suspended": {
+ "description": "Suspended",
+ "value": "2"
+ },
+ "delinquent": {
+ "description": "Delinquent",
+ "value": "3"
+ },
+ "violation": {
+ "description": "Copyright Violation",
+ "value": "4"
+ }
+ }
+ }
+ },
+ "default": "enabled"
+ },
+ "demo": {
+ "kind": "leaf",
+ "type": "boolean",
+ "default": "false"
+ },
+ "uplink-speed": {
+ "kind": "leaf",
+ "type": {
+ "dev:bandwidth": {
+ "range": "1000000..max"
+ }
+ },
+ "default": "1000000000",
+ "units": "bps"
+ },
+ "downlink-speed": {
+ "kind": "leaf",
+ "type": {
+ "dev:bandwidth": {
+ "range": "1000000..max"
+ }
+ },
+ "default": "1000000000",
+ "units": "bps"
+ },
+ "id": {
+ "kind": "leaf",
+ "type": {
+ "unique-identifier": {
+ "type": {
+ "uint32": {
+ "range": "1..max"
+ },
+ "yang:uuid": {
+ "pattern": "/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/"
+ }
+ }
+ }
+ },
+ "mandatory": true
+ },
+ "kind": {
+ "kind": "leaf",
+ "type": {
+ "identityref": {
+ "base": "kind"
+ }
+ },
+ "default": "generic"
+ },
+ "name": {
+ "kind": "leaf",
+ "type": {
+ "string": {
+ "length": "0..255"
+ }
+ },
+ "description": "Specify name of the TenantRoot"
+ },
+ "service-specific-attribute": {
+ "kind": "leaf",
+ "type": "string"
+ },
+ "service-specific-id": {
+ "kind": "leaf",
+ "type": {
+ "unique-identifier": {
+ "type": {
+ "uint32": {
+ "range": "1..max"
+ },
+ "yang:uuid": {
+ "pattern": "/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/"
+ }
+ }
+ }
+ },
+ "mandatory": true
+ },
+ "delete": {
+ "kind": "action"
+ },
+ "services": {
+ "kind": "container",
+ "description": "Contains various services available to the subscriber"
+ },
+ "device": {
+ "kind": "list",
+ "key": "mac"
+ },
+ "subscribed-tenants": {
+ "kind": "list",
+ "config": false
+ }
+ }
+}
+```
+
+### Additional Endpoints
+
+There are a number of additional operational endpoints dynamically
+rendered for the YANG data model. Since YANG is a **hierarchical**
+data modeling language, you can have deeply nested structures of
+various `containers` and `lists`. The appropriate CRUD operations can
+take effect as deep into the endpoint entity hierarchy according to
+the YANG schema.
+
+Here's a sample list of various URIs for the Subscriber model:
+
+Methods | Endpoint URI
+--- | ---
+GET/POST | /cord:subscriber
+GET/PUT/DELETE | /cord:subscriber/:id
+GET/PUT | /cord:subscriber/:id/services/cdn
+GET/POST | /cord:subscriber/:id/device
+GET/PUT/DELETE | /cord:subscriber/:id/device/:mac
+GET/PUT | /cord:subscriber/:id/device/:mac/features
+
+### Validations
+
+Whenever POST/PUT requests are transacted, the incoming data is YANG
+schema validated to ensure fitness of the data before it gets applied
+into the runtime configuration data state.
+
+For example, if you tried to POST /cord:subscriber without the
+`mandatory` *id* and *service-specific-id* properties, it will
+generate an error and reject the request.
+
+Also, if you tried to PUT using data values that do not conform to the
+underlying YANG schema for that data element, it will generate an
+error and reject the requst.
+
+Invalid Request:
+```bash
+$ curl -X PUT localhost:5050/cord:subscriber/10 -H 'content-type: application/json' -d '{ "status": false }'
+```
+Error Response:
+```json
+{
+ "error": {
+ "message": "[enumeration] type violation for 'false' on enabled,suspended,delinquent,violation"
+ }
+}
+```
+
+For additional information on comprehensive validation enforcement
+support, please refer to [yang-js](http://github.com/corenova/yang-js)
+YANG compliance status report.
+
diff --git a/src/cord-core.coffee b/src/cord-core.coffee
new file mode 100644
index 0000000..abbff2f
--- /dev/null
+++ b/src/cord-core.coffee
@@ -0,0 +1,17 @@
+#
+# Author: Peter K. Lee (peter@corenova.com)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+require('yang-js').register()
+
+module.exports = require('../schema/cord-core.yang').bind {
+
+ '/cord:subscriber/label': -> "cordSubscriber-#{@get '../id'}"
+
+ '/cord:subscriber/delete': (input, resolve, reject) -> reject "not yet implemented"
+}
diff --git a/src/cord-device.coffee b/src/cord-device.coffee
new file mode 100644
index 0000000..ce6c892
--- /dev/null
+++ b/src/cord-device.coffee
@@ -0,0 +1,16 @@
+#
+# Author: Peter K. Lee (peter@corenova.com)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+require('yang-js').register()
+
+module.exports = require('../schema/cord-device.yang').bind {
+
+ '/{device}/features/update': (input, resolve, reject) -> reject "not yet implemented"
+
+}
diff --git a/src/cord-subscriber.coffee b/src/cord-subscriber.coffee
new file mode 100644
index 0000000..7563aa7
--- /dev/null
+++ b/src/cord-subscriber.coffee
@@ -0,0 +1,32 @@
+#
+# Author: Peter K. Lee (peter@corenova.com)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+require('yang-js').register()
+
+module.exports = require('../schema/cord-subscriber.yang').bind {
+
+ '/{subscriber}':
+ # auto-computed properties
+ 'device/subscriber': -> @get '../..'
+
+ # action bindings
+ 'device/create': (input, resolve, reject) -> reject "TBD"
+ 'device/update': (input, resolve, reject) -> reject "TBD"
+ 'device/delete': (input, resolve, reject) -> reject "TBD"
+
+ '/{subscriber-controller}':
+ # auto-computed properties
+ 'related': -> new Error "will return related objects once implemented"
+
+ # action bindings
+ 'features/update': (input, resolve, reject) -> reject "TBD"
+ 'identity/update': (input, resolve, reject) -> reject "TBD"
+
+}
+
diff --git a/src/server.coffee b/src/server.coffee
new file mode 100644
index 0000000..37f7345
--- /dev/null
+++ b/src/server.coffee
@@ -0,0 +1,32 @@
+#
+# Author: Peter K. Lee (peter@corenova.com)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+Yang = require('yang-js')
+
+app = require('yang-express') ->
+ @enable 'yangapi'
+ @enable 'openapi', require('../package.json')
+ @enable 'restjson'
+ @enable 'websocket'
+
+ @open 'cord', ->
+ @import Yang.require('cord-core')
+ @import Yang.require('xos-core')
+
+ @connect require('../sample-data.json')
+ @on 'update', (prop) ->
+ console.log "[#{prop.path}] got updated, should consider persisting the change somewhere"
+
+module.exports = app
+
+# only start if directly invoked
+if require.main is module
+ argv = require('minimist')(process.argv.slice(2))
+ argv.port ?= 5050
+ app.listen argv.port
diff --git a/test/mocha.opts b/test/mocha.opts
new file mode 100644
index 0000000..38f602d
--- /dev/null
+++ b/test/mocha.opts
@@ -0,0 +1,3 @@
+--require should
+--compilers coffee:coffee-script/register
+--sort
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..92b0c18
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,5 @@
+var path = require('path');
+
+module.exports = {
+ entry: "./lib/client/entry.js"
+};