added copy of github.com/opnfv/promise source into the source directory 19/8719/1
authorPeter Lee <peter@corenova.com>
Fri, 29 Jan 2016 23:21:56 +0000 (15:21 -0800)
committerGerald Kunzmann <kunzmann@docomolab-euro.com>
Fri, 29 Jan 2016 23:48:40 +0000 (23:48 +0000)
Change-Id: Ib212302a4132aa492f7b701a7ca02f54a7d0a6af
(cherry picked from commit a46af646972b6ff263fb207d28a59e7ce7417b5c)

26 files changed:
source/.gitignore [new file with mode: 0644]
source/.npmignore [new file with mode: 0644]
source/LICENSE [new file with mode: 0644]
source/README.md [new file with mode: 0644]
source/config/custom-environment-variables.yaml [new file with mode: 0644]
source/config/default.yaml [new file with mode: 0644]
source/config/demo.json [new file with mode: 0644]
source/config/functest.yaml [new file with mode: 0644]
source/config/test-intercloud.yaml [new file with mode: 0644]
source/forge.yaml [new file with mode: 0644]
source/index.yaml [new file with mode: 0644]
source/openstack.yaml [new file with mode: 0644]
source/package.json [new file with mode: 0644]
source/promise.yaml [new file with mode: 0644]
source/schema/access-control-models.yang [new file with mode: 0644]
source/schema/nfv-infrastructure.yang [new file with mode: 0644]
source/schema/nfv-mano.yang [new file with mode: 0644]
source/schema/openstack-compute.yang [new file with mode: 0644]
source/schema/openstack-identity.yang [new file with mode: 0644]
source/schema/openstack.yang [new file with mode: 0644]
source/schema/opnfv-promise.yang [new file with mode: 0644]
source/spec/openstack-intents.coffee [new file with mode: 0644]
source/spec/promise-intents.coffee [new file with mode: 0644]
source/spec/promise-module.coffee [new file with mode: 0644]
source/test/mocha.opts [new file with mode: 0644]
source/test/promise-intents.coffee [new file with mode: 0644]

diff --git a/source/.gitignore b/source/.gitignore
new file mode 100644 (file)
index 0000000..f49cc53
--- /dev/null
@@ -0,0 +1,16 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+node_modules
+npm-debug.log
+*~
+*#
+
diff --git a/source/.npmignore b/source/.npmignore
new file mode 100644 (file)
index 0000000..4f726b0
--- /dev/null
@@ -0,0 +1,2 @@
+.git*
+
diff --git a/source/LICENSE b/source/LICENSE
new file mode 100644 (file)
index 0000000..8f71f43
--- /dev/null
@@ -0,0 +1,202 @@
+                                 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/source/README.md b/source/README.md
new file mode 100644 (file)
index 0000000..681e2b9
--- /dev/null
@@ -0,0 +1,144 @@
+# Resource Management for Virtual Infrastructure
+
+**Promise** is a resource reservation and management project to identify NFV related requirements and realize resource reservation for future usage by capacity management of resource pools regarding compute, network and storage.
+
+The following are the key features provided by this module:
+
+* Resource Capacity Management
+* Resource Reservation
+* Resource Allocation
+
+This module also contains a collection of [YANG data models](schema/) as defined under the direction of [OPNFV Promise](http://wiki.opnfv.org/promise) project.
+
+## Installation
+
+`opnfv-promise` is built with [YangForge](http://github.com/opnfv/yangforge) data modeling
+framework. You will need to first install `yangforge` and use the
+provided `yfc` command line utility to run this module.
+
+```bash
+$ npm install -g yangforge
+```
+
+There are also alternative installer plugins for [Fuel](http://github.com/opnfv/fuel-plugin-promise) and [Juju](http://github.com/opnfv/juju-plugin-promise).
+
+## Usage
+```bash
+$ yfc run promise.yaml
+```
+
+The `yfc run` command will load the primary application
+package from this repository along with any other dependency
+files/assets referenced within the YAML manifest and instantiate the
+opnfv-promise module and run REST/JSON interface by default listening
+on port 5000.
+
+You can also checkout this GIT repository or simply download the files
+into your local system and run the application.
+
+## Testing
+
+```bash
+$ npm install
+$ npm test
+```
+
+TBD
+
+## Primary YANG Data Models
+
+name | description | status
+--- | --- | ---
+[opnfv-promise](schema/opnfv-promise.yang) | provide resource reservation and capacity management | 95% complete
+[nfv-infrastructure](schema/nfv-infrastructure.yang) | common NFV Infrastructure resource models | 80% complete
+[nfv-mano](schema/nfv-mano.yang) | common NFV MANO resource models including VIM | 20% complete
+[openstack](schema/openstack.yang) | openstack specific VIM extensions | 50% complete
+
+## Promise Information Models
+
+### ResourceReservation
+
+The data model describing the required parameters regarding a resource
+reservation. The schema definition expressed in Yang can be found
+[here](schema/opnfv-promise.yang).
+
+#### Key Elements
+
+Name | Type | Description
+---  | ---  | ---
+start | ys:date-and-time | Timestamp of when the consumption of reserved resources can begin
+end   | ys:date-and-time | Timestamp of when the consumption of reserved resource must end
+expiry | number | Duration expressed in seconds since `start` when resource not yet allocated shall be released back to the available zone
+zone | nfvi:AvailabilityZone | Reference to a zone where the resources will be reserved
+capacity | object | Quantity of resources to be reserved per resource types
+attributes | list | References to resource attributes needed for reservation
+resources | list (nfvi:ResourceElement) | Reference to a collection of existing resource elements required
+
+#### State Elements (read-only)
+
+State Elements are available as part of lookup response about the data model.
+
+Name | Type | Description
+---  | ---  | ---
+provider | nfvi:ResourceProvider | Reference to a specific provider when reservation service supports multiple providers
+remaining | object | Quantity of resources remaining for consumption based on consumed allocations
+allocations | list (nfvi:ResourceAllocation) | Reference to a collection of consumed allocations referencing this reservation
+
+#### Notification Elements
+
+Name | Type | Description
+---  | ---  | ---
+reservation-event | Event | Subscribers will be notified if the reservation encounters an error or other events
+
+#### Inherited Elements
+
+##### Extended from [nfvi:ResourceElement](schema/nfv-infrastructure.yang)
+
+Name | Type | Description
+---  | ---  | ---
+id | yang:uuid | A GUID identifier for the data model (usually auto-generated, but can also be specified)
+name | string | Name of the data model
+enabled | boolean | Enable/Disable the data model
+protected | boolean | Prevent model from being destroyed when protected
+owner | nfvi:AccessIdentity | An owner for the data model
+visibility | enumeration | Visibility level of the given data model
+tags | list (string) | List of string tags for query/filter
+members | list (nfvi:AccessIdentity) | List of additional AccessIdentities that can operate on the data model
+
+### Resource Allocation
+
+The data model describing the required parameters regarding a resource
+allocation.  The schema definition expressed in YANG can be found
+[here](schema/opnfv-promise.yang).
+
+#### Key Elements
+
+Name | Type | Description
+---  | ---  | ---
+reservation | nfvi:ResourceReservation | Reference to an existing reservation identifier
+allocate-on-start | boolean | Specify whether the allocation can take effect automatically upon reservation 'start'
+resources | list (nfvi:ResourceElement) | Reference to a collection of new resource elements to be allocated
+
+#### State Elements (read-only)
+
+Name | Type | Description
+---  | ---  | ---
+priority | number | Read-only state information about the priority classification of the reservation
+
+#### Inherited Elements
+
+##### Extended from [nfvi:ResourceElement](schema/nfv-infrastructure.yang)
+
+Name | Type | Description
+---  | ---  | ---
+id | yang:uuid | A GUID identifier for the data model (usually auto-generated, but can also be specified)
+name | string | Name of the data model
+enabled | boolean | Enable/Disable the data model
+protected | boolean | Prevent model from being destroyed when protected
+owner | nfvi:AccessIdentity | An owner for the data model
+visibility | enumeration | Visibility level of the given data model
+tags | list (string) | List of string tags for query/filter
+members | list (nfvi:AccessIdentity) | List of additional AccessIdentities that can operate on the data model
+
+## License
+  [Apache-2.0](LICENSE)
diff --git a/source/config/custom-environment-variables.yaml b/source/config/custom-environment-variables.yaml
new file mode 100644 (file)
index 0000000..30a72d2
--- /dev/null
@@ -0,0 +1,23 @@
+# OPNFV FuncTest config (refer to schema/opnfv-functest.yang)
+opnfv-functest:
+  environment:
+    installer:
+      type: INSTALLER_TYPE
+      address: INSTALLER_IP
+    lab: NODE_NAME
+
+# OpenStack config (native)
+openstack:
+  auth:
+    endpoint: OS_AUTH_URL
+    strategy: OS_AUTH_STRATEGY
+    tenant:
+      id: OS_TENANT_ID
+      name: OS_TENANT_NAME
+    username: OS_USERNAME
+    password: OS_PASSWORD
+  test:
+    image:  OS_TEST_IMAGE
+    flavor: OS_TEST_FLAVOR
+    network: OS_TEST_NETWORK
+    
diff --git a/source/config/default.yaml b/source/config/default.yaml
new file mode 100644 (file)
index 0000000..52bb61a
--- /dev/null
@@ -0,0 +1,11 @@
+# default configuration for 'npm test'
+
+opnfv-promise:
+  promise:
+    policy:
+      reservation:
+        max-future-start-range: 
+        max-future-end-range: 
+        max-duration:
+        expiry: 600
+
diff --git a/source/config/demo.json b/source/config/demo.json
new file mode 100644 (file)
index 0000000..dffb3af
--- /dev/null
@@ -0,0 +1,118 @@
+{
+  "opnfv-promise": {
+    "promise": {
+      "providers": [
+        {
+          "name": "example-demo-provider",
+          "token": "dummy-token"
+        }
+      ],
+      "pools": [
+        {
+          "ResourcePool": {
+            "id": "4085f0da-8030-4252-a0ff-c6f93870eb5f",
+            "name": "OPNFV OpenStack - West",
+            "source": "example-demo-provider",
+            "capacity": {
+              "cores": 100,
+              "ram": 262144,
+              "instances": 500,
+              "networks": 100,
+              "ports": 100,
+              "routers": 30,
+              "subnets": 1000,
+              "addresses": 500,
+              "gigabytes": 10000,
+              "snapshots": 100,
+              "volumes": 100
+            }
+          }
+        }
+      ],
+      "reservations": [
+        {
+          "capacity": {
+            "cores": 10,
+            "ram": 4096,
+            "instances": 10,
+            "networks": 4,
+            "ports": 10,
+            "routers": 1,
+            "subnets": 1,
+            "addresses": 10,
+            "gigabytes": 0,
+            "snapshots": 0,
+            "volumes": 0
+          },
+          "start": "2015-11-07T10:17:12.747Z",
+          "end": "2016-02-13T10:17:18.226Z",
+          "pools": [
+            "4085f0da-8030-4252-a0ff-c6f93870eb5f"
+          ]
+        },
+        {
+          "capacity": {
+            "cores": 20,
+            "ram": 10000,
+            "instances": 5,
+            "networks": 2,
+            "ports": 10,
+            "routers": 1,
+            "subnets": 1,
+            "addresses": 5,
+            "gigabytes": 0,
+            "snapshots": 0,
+            "volumes": 0
+          },
+          "start": "2015-11-09T10:17:12.747Z",
+          "end": "2016-02-11T10:17:18.226Z",
+          "pools": [
+            "4085f0da-8030-4252-a0ff-c6f93870eb5f"
+          ]
+        },
+        {
+          "id": "c7287f30-2c65-4a88-a047-48724b8ff747",
+          "capacity": {
+            "cores": 10,
+            "ram": 4096,
+            "instances": 10,
+            "networks": 5,
+            "ports": 10,
+            "routers": 1,
+            "subnets": 5,
+            "addresses": 20,
+            "gigabytes": 0,
+            "snapshots": 0,
+            "volumes": 0
+          },
+          "start": "2015-11-10T10:17:12.747Z",
+          "end": "2015-12-13T10:17:18.226Z",
+          "pools": [
+            "4085f0da-8030-4252-a0ff-c6f93870eb5f"
+          ]
+        },
+        {
+          "id": "0f2e31f7-9760-416d-8d53-1ee68aa4b11f",
+          "capacity": {
+            "cores": 10,
+            "ram": 4096,
+            "instances": 5,
+            "networks": 2,
+            "ports": 10,
+            "routers": 1,
+            "subnets": 1,
+            "addresses": 5,
+            "gigabytes": 0,
+            "snapshots": 0,
+            "volumes": 0
+          },
+          "start": "2015-11-09T10:17:12.747Z",
+          "end": "2015-12-03T10:17:18.226Z",
+          "pools": [
+            "4085f0da-8030-4252-a0ff-c6f93870eb5f"
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/source/config/functest.yaml b/source/config/functest.yaml
new file mode 100644 (file)
index 0000000..8cc84d3
--- /dev/null
@@ -0,0 +1,9 @@
+# NODE_ENV=functest
+
+opnfv-functest:
+  environment:
+    images:
+      -
+        name: cirros
+        path: /home/opnfv/functest/data/cirros-0.3.4-x86_64-disk.img
+
diff --git a/source/config/test-intercloud.yaml b/source/config/test-intercloud.yaml
new file mode 100644 (file)
index 0000000..f5e04ed
--- /dev/null
@@ -0,0 +1,21 @@
+# the following config is used when ENV is as follows:
+# NODE_ENV=test
+# NODE_APP_INSTANCE=intercloud
+openstack:
+  auth:
+    strategy: keystone
+    endpoint: http://vhub4.intercloud.net:5000/v2.0
+    tenant:
+      id: 62a2d90992114994977fd6707bac5758
+    username: peter
+    password: # set OS_PASSWORD=xxxx environmental variable
+  test:
+    image:  ee0fb445-0fc2-4fda-a2dc-175bf3cc3cb1
+    flavor: 2312fd98-369e-4361-b967-606373891c11
+
+opnfv-promise:
+  promise:
+    policy:
+      reservation:
+        max-future-start-range: 7
+        max-duration: 24
diff --git a/source/forge.yaml b/source/forge.yaml
new file mode 100644 (file)
index 0000000..24317d1
--- /dev/null
@@ -0,0 +1,42 @@
+# YF 0.12.x forge manifest
+
+compilers:
+  yang: yangforge/register
+  coffee: coffee-script/register
+
+components:
+  nfvi:
+    - yangforge:common
+    - schema/access-control-models.yang
+    - schema/nfv-infrastructure.yang
+    - schema/nfv-mano.yang
+
+  # primary promise service
+  promise:
+    - nfvi
+    - schema/opnfv-promise.yang
+    - spec/promise.yaml
+
+  # base openstack composition
+  openstack:
+    - nfvi
+    - schema/openstack.yang
+    - schema/openstack-identity.yang
+    - schema/openstack-image.yang
+    - schema/openstack-compute.yang
+    - schema/openstack-storage.yang
+    - schema/openstack-network.yang
+    - spec/openstack.yaml
+
+  # openstack with promise augmentation
+  os-promise:
+    - promise
+    - openstack
+    - schema/openstack-promise.yang
+    - spec/openstack-promise.yaml
+
+  # test component for using with 'npm test'
+  test:
+    - os-promise
+    - schema/opnfv-functest.yang
+    - config/demo.json
diff --git a/source/index.yaml b/source/index.yaml
new file mode 100644 (file)
index 0000000..071d685
--- /dev/null
@@ -0,0 +1,4120 @@
+synth: source
+name: opnfv-promise
+version: !<tag:yaml.org,2002:js/undefined> ''
+description: Resource Management for Virtualized Infrastructure
+license: Apache-2.0
+schema:
+  module:
+    opnfv-promise:
+      namespace: 'urn:opnfv:promise'
+      prefix: promise
+      import:
+        complex-types:
+          prefix: ct
+        ietf-yang-types:
+          prefix: yang
+        ietf-inet-types:
+          prefix: inet
+        access-control-models:
+          prefix: acm
+        nfv-infrastructure:
+          prefix: nfvi
+      description: OPNFV Promise Resource Reservation/Allocation controller module
+      revision:
+        '2015-10-05':
+          description: Complete coverage of reservation related intents
+        '2015-08-06':
+          description: Updated to incorporate YangForge framework
+        '2015-04-16':
+          description: Initial revision.
+      feature:
+        reservation-service:
+          description: 'When enabled, provides resource reservation service'
+        multi-provider:
+          description: 'When enabled, provides resource management across multiple providers'
+      grouping:
+        resource-utilization:
+          container:
+            capacity:
+              container:
+                total:
+                  description: Conceptual container that should be extended
+                reserved:
+                  description: Conceptual container that should be extended
+                  config: false
+                usage:
+                  description: Conceptual container that should be extended
+                  config: false
+                available:
+                  description: Conceptual container that should be extended
+                  config: false
+        temporal-resource-collection:
+          description: Information model capturing resource-collection with start/end time window
+          leaf:
+            start:
+              type: 'yang:date-and-time'
+            end:
+              type: 'yang:date-and-time'
+          container:
+            capacity:
+              uses: {}
+              leaf:
+                cores:
+                  type: int16
+                  default: '0'
+                ram:
+                  type: int32
+                  default: '0'
+                  units: MB
+                instances:
+                  type: int16
+                  default: '0'
+                networks:
+                  type: int16
+                  default: '0'
+                ports:
+                  type: int16
+                  default: '0'
+                routers:
+                  type: int16
+                  default: '0'
+                subnets:
+                  type: int16
+                  default: '0'
+                addresses:
+                  type: int32
+                  default: '0'
+                gigabytes:
+                  type: int32
+                  default: '0'
+                  units: GB
+                snapshots:
+                  type: int16
+                  default: '0'
+                volumes:
+                  type: int16
+                  default: '0'
+          leaf-list:
+            elements:
+              type:
+                instance-identifier:
+                  'ct:instance-type': 'nfvi:ResourceElement'
+                  require-instance: true
+        resource-usage-request:
+          description: |-
+            Information model capturing available parameters to make a resource
+            usage request.
+          reference: 'OPNFV-PROMISE, Section 3.4.1'
+          uses: {}
+          leaf:
+            zone:
+              description: Optional identifier to an Availability Zone
+              type:
+                instance-identifier:
+                  'ct:instance-type': 'nfvi:AvailabilityZone'
+            start:
+              type: 'yang:date-and-time'
+            end:
+              type: 'yang:date-and-time'
+          container:
+            capacity:
+              uses: {}
+              leaf:
+                cores:
+                  type: int16
+                  default: '0'
+                ram:
+                  type: int32
+                  default: '0'
+                  units: MB
+                instances:
+                  type: int16
+                  default: '0'
+                networks:
+                  type: int16
+                  default: '0'
+                ports:
+                  type: int16
+                  default: '0'
+                routers:
+                  type: int16
+                  default: '0'
+                subnets:
+                  type: int16
+                  default: '0'
+                addresses:
+                  type: int32
+                  default: '0'
+                gigabytes:
+                  type: int32
+                  default: '0'
+                  units: GB
+                snapshots:
+                  type: int16
+                  default: '0'
+                volumes:
+                  type: int16
+                  default: '0'
+          leaf-list:
+            elements:
+              type:
+                instance-identifier:
+                  'ct:instance-type': 'nfvi:ResourceElement'
+                  require-instance: true
+              description: |-
+                Reference to a list of 'pre-existing' resource elements that are
+                required for fulfillment of the resource-usage-request.
+
+                It can contain any instance derived from ResourceElement,
+                such as ServerInstances or even other
+                ResourceReservations. If the resource-usage-request is
+                accepted, the ResourceElement(s) listed here will be placed
+                into 'protected' mode as to prevent accidental removal.
+
+                If any of these resource elements become 'unavailable' due to
+                environmental or administrative activity, a notification will
+                be issued informing of the issue.
+        query-start-end-window:
+          container:
+            window:
+              description: Matches entries that are within the specified start/end time window
+              leaf:
+                start:
+                  type: 'yang:date-and-time'
+                end:
+                  type: 'yang:date-and-time'
+                scope:
+                  type:
+                    enumeration:
+                      enum:
+                        exclusive:
+                          description: Matches entries that start AND end within the window
+                          value: 0
+                        inclusive:
+                          description: Matches entries that start OR end within the window
+                          value: 1
+                  default: inclusive
+        query-resource-collection:
+          uses: {}
+          leaf-list:
+            without:
+              description: Excludes specified collection identifiers from the result
+              type:
+                instance-identifier:
+                  'ct:instance-type': ResourceCollection
+          leaf:
+            show-utilization:
+              type: boolean
+              default: 'true'
+          container:
+            elements:
+              leaf-list:
+                some:
+                  description: Query for ResourceCollection(s) that contain some or more of these element(s)
+                  type:
+                    instance-identifier:
+                      'ct:instance-type': 'nfvi:ResourceElement'
+                every:
+                  description: Query for ResourceCollection(s) that contain all of these element(s)
+                  type:
+                    instance-identifier:
+                      'ct:instance-type': 'nfvi:ResourceElement'
+            window:
+              description: Matches entries that are within the specified start/end time window
+              leaf:
+                start:
+                  type: 'yang:date-and-time'
+                end:
+                  type: 'yang:date-and-time'
+                scope:
+                  type:
+                    enumeration:
+                      enum:
+                        exclusive:
+                          description: Matches entries that start AND end within the window
+                          value: 0
+                        inclusive:
+                          description: Matches entries that start OR end within the window
+                          value: 1
+                  default: inclusive
+        common-intent-output:
+          leaf:
+            result:
+              type:
+                enumeration:
+                  enum:
+                    ok:
+                      value: 0
+                    conflict:
+                      value: 1
+                    error:
+                      value: 2
+            message:
+              type: string
+        utilization-output:
+          list:
+            utilization:
+              key: timestamp
+              leaf:
+                timestamp:
+                  type: 'yang:date-and-time'
+                count:
+                  type: int16
+              container:
+                capacity:
+                  uses: {}
+                  leaf:
+                    cores:
+                      type: int16
+                      default: '0'
+                    ram:
+                      type: int32
+                      default: '0'
+                      units: MB
+                    instances:
+                      type: int16
+                      default: '0'
+                    networks:
+                      type: int16
+                      default: '0'
+                    ports:
+                      type: int16
+                      default: '0'
+                    routers:
+                      type: int16
+                      default: '0'
+                    subnets:
+                      type: int16
+                      default: '0'
+                    addresses:
+                      type: int32
+                      default: '0'
+                    gigabytes:
+                      type: int32
+                      default: '0'
+                      units: GB
+                    snapshots:
+                      type: int16
+                      default: '0'
+                    volumes:
+                      type: int16
+                      default: '0'
+      'ct:complex-type':
+        ResourceCollection:
+          'ct:extends': 'nfvi:ResourceContainer'
+          'ct:abstract': 'true'
+          description: |-
+            Describes an abstract ResourceCollection data model, which represents
+            a grouping of capacity and elements available during a given
+            window in time which must be extended by other resource
+            collection related models
+          leaf:
+            start:
+              type: 'yang:date-and-time'
+            end:
+              type: 'yang:date-and-time'
+            active:
+              config: false
+              description: |-
+                Provides current state of this record whether it is enabled and within
+                specified start/end time
+              type: boolean
+        ResourcePool:
+          'ct:extends': ResourceCollection
+          description: |-
+            Describes an instance of an active ResourcePool record, which
+            represents total available capacity and elements from a given
+            source.
+          leaf:
+            source:
+              type:
+                instance-identifier:
+                  'ct:instance-type': 'nfvi:ResourceContainer'
+                  require-instance: true
+              mandatory: true
+          refine:
+            elements:
+              must:
+                'boolean(/source/elements/*[@id=id])':
+                  error-message: One or more of the ResourceElement(s) does not exist in the provider to be reserved
+        ResourceReservation:
+          'ct:extends': ResourceCollection
+          description: |-
+            Describes an instance of an accepted resource reservation request,
+            created usually as a result of 'create-reservation' request.
+
+            A ResourceReservation is a derived instance of a generic
+            ResourceCollection which has additional parameters to map the
+            pool(s) that were referenced to accept this reservation as well
+            as to track allocations made referencing this reservation.
+
+            Contains the capacities of various resource attributes being
+            reserved along with any resource elements that are needed to be
+            available at the time of allocation(s).
+          reference: 'OPNFV-PROMISE, Section 3.4.1'
+          leaf:
+            created-on:
+              type: 'yang:date-and-time'
+              config: false
+            modified-on:
+              type: 'yang:date-and-time'
+              config: false
+          leaf-list:
+            pools:
+              config: false
+              description: |-
+                Provides list of one or more pools that were referenced for providing
+                the requested resources for this reservation.  This is an
+                important parameter for informing how/where allocation
+                requests can be issued using this reservation since it is
+                likely that the total reserved resource capacity/elements are
+                made availble from multiple sources.
+              type:
+                instance-identifier:
+                  'ct:instance-type': ResourcePool
+                  require-instance: true
+            allocations:
+              config: false
+              description: |-
+                Reference to a collection of consumed allocations referencing
+                this reservation.
+              type:
+                instance-identifier:
+                  'ct:instance-type': ResourceAllocation
+                  require-instance: true
+          container:
+            remaining:
+              config: false
+              description: |-
+                Provides visibility into total remaining capacity for this
+                reservation based on allocations that took effect utilizing
+                this reservation ID as a reference.
+              uses: {}
+              leaf:
+                cores:
+                  type: int16
+                  default: '0'
+                ram:
+                  type: int32
+                  default: '0'
+                  units: MB
+                instances:
+                  type: int16
+                  default: '0'
+                networks:
+                  type: int16
+                  default: '0'
+                ports:
+                  type: int16
+                  default: '0'
+                routers:
+                  type: int16
+                  default: '0'
+                subnets:
+                  type: int16
+                  default: '0'
+                addresses:
+                  type: int32
+                  default: '0'
+                gigabytes:
+                  type: int32
+                  default: '0'
+                  units: GB
+                snapshots:
+                  type: int16
+                  default: '0'
+                volumes:
+                  type: int16
+                  default: '0'
+        ResourceAllocation:
+          'ct:extends': ResourceCollection
+          description: |-
+            A ResourceAllocation record denotes consumption of resources from a
+            referenced ResourcePool.
+
+            It does not reflect an accepted request but is created to
+            represent the actual state about the ResourcePool. It is
+            created once the allocation(s) have successfully taken effect
+            on the 'source' of the ResourcePool.
+
+            The 'priority' state indicates the classification for dealing
+            with resource starvation scenarios. Lower priority allocations
+            will be forcefully terminated to allow for higher priority
+            allocations to be fulfilled.
+
+            Allocations without reference to an existing reservation will
+            receive the lowest priority.
+          reference: 'OPNFV-PROMISE, Section 3.4.3'
+          leaf:
+            reservation:
+              description: Reference to an existing reservation identifier (optional)
+              type:
+                instance-identifier:
+                  'ct:instance-type': ResourceReservation
+                  require-instance: true
+            pool:
+              description: Reference to an existing resource pool from which allocation is drawn
+              type:
+                instance-identifier:
+                  'ct:instance-type': ResourcePool
+                  require-instance: true
+            priority:
+              config: false
+              description: Reflects current priority level of the allocation according to classification rules
+              type:
+                enumeration:
+                  enum:
+                    high:
+                      value: 1
+                    normal:
+                      value: 2
+                    low:
+                      value: 3
+              default: normal
+          container:
+            instance-ref:
+              config: false
+              description: Reference to actual instance identifier of the provider/server for this allocation
+              leaf:
+                provider:
+                  type:
+                    instance-identifier:
+                      'ct:instance-type': ResourceProvider
+                server:
+                  type: 'yang:uuid'
+        ResourceFlavor:
+          description: currently NOT an extension of ResourceElement.
+          key: id
+          leaf:
+            id:
+              type: string
+            name:
+              type: string
+            disk:
+              type: uint32
+              units: GB
+              default: '0'
+            ram:
+              type: uint32
+              units: MB
+              default: '0'
+            vcpus:
+              type: uint16
+              default: '0'
+        ResourceProvider:
+          'ct:extends': 'nfvi:ResourceContainer'
+          leaf:
+            token:
+              type: string
+              mandatory: true
+          container:
+            services:
+              config: false
+              container:
+                compute:
+                  leaf:
+                    endpoint:
+                      type: 'inet:uri'
+                  'ct:instance-list':
+                    flavors:
+                      'ct:instance-type': ResourceFlavor
+          leaf-list:
+            pools:
+              config: false
+              description: Provides list of one or more pools that are referencing this provider.
+              type:
+                instance-identifier:
+                  'ct:instance-type': ResourcePool
+                  require-instance: true
+      container:
+        promise:
+          uses: {}
+          'ct:instance-list':
+            providers:
+              if-feature: multi-provider
+              description: Aggregate collection of all registered ResourceProvider instances for Promise resource management service
+              'ct:instance-type': ResourceProvider
+              status: unavailable
+            pools:
+              if-feature: reservation-service
+              description: Aggregate collection of all ResourcePool instances
+              'ct:instance-type': ResourcePool
+              status: unavailable
+            reservations:
+              if-feature: reservation-service
+              description: Aggregate collection of all ResourceReservation instances
+              'ct:instance-type': ResourceReservation
+              status: unavailable
+            allocations:
+              description: Aggregate collection of all ResourceAllocation instances
+              'ct:instance-type': ResourceAllocation
+          container:
+            policy:
+              container:
+                reservation:
+                  leaf:
+                    max-future-start-range:
+                      description: "Enforce reservation request 'start' time is within allowed range from now"
+                      type:
+                        uint16:
+                          range: 0..365
+                      units: days
+                    max-future-end-range:
+                      description: "Enforce reservation request 'end' time is within allowed range from now"
+                      type:
+                        uint16:
+                          range: 0..365
+                      units: days
+                    max-duration:
+                      description: Enforce reservation duration (end-start) does not exceed specified threshold
+                      type: uint16
+                      units: hours
+                      default: '8760'
+                    expiry:
+                      description: |-
+                        Duration in minutes from start when unallocated reserved resources
+                        will be released back into the pool
+                      type: uint32
+                      units: minutes
+            capacity:
+              container:
+                total:
+                  description: Conceptual container that should be extended
+                  uses: {}
+                  leaf:
+                    cores:
+                      type: int16
+                      default: '0'
+                    ram:
+                      type: int32
+                      default: '0'
+                      units: MB
+                    instances:
+                      type: int16
+                      default: '0'
+                    networks:
+                      type: int16
+                      default: '0'
+                    ports:
+                      type: int16
+                      default: '0'
+                    routers:
+                      type: int16
+                      default: '0'
+                    subnets:
+                      type: int16
+                      default: '0'
+                    addresses:
+                      type: int32
+                      default: '0'
+                    gigabytes:
+                      type: int32
+                      default: '0'
+                      units: GB
+                    snapshots:
+                      type: int16
+                      default: '0'
+                    volumes:
+                      type: int16
+                      default: '0'
+                reserved:
+                  description: Conceptual container that should be extended
+                  config: false
+                  uses: {}
+                  leaf:
+                    cores:
+                      type: int16
+                      default: '0'
+                    ram:
+                      type: int32
+                      default: '0'
+                      units: MB
+                    instances:
+                      type: int16
+                      default: '0'
+                    networks:
+                      type: int16
+                      default: '0'
+                    ports:
+                      type: int16
+                      default: '0'
+                    routers:
+                      type: int16
+                      default: '0'
+                    subnets:
+                      type: int16
+                      default: '0'
+                    addresses:
+                      type: int32
+                      default: '0'
+                    gigabytes:
+                      type: int32
+                      default: '0'
+                      units: GB
+                    snapshots:
+                      type: int16
+                      default: '0'
+                    volumes:
+                      type: int16
+                      default: '0'
+                usage:
+                  description: Conceptual container that should be extended
+                  config: false
+                  uses: {}
+                  leaf:
+                    cores:
+                      type: int16
+                      default: '0'
+                    ram:
+                      type: int32
+                      default: '0'
+                      units: MB
+                    instances:
+                      type: int16
+                      default: '0'
+                    networks:
+                      type: int16
+                      default: '0'
+                    ports:
+                      type: int16
+                      default: '0'
+                    routers:
+                      type: int16
+                      default: '0'
+                    subnets:
+                      type: int16
+                      default: '0'
+                    addresses:
+                      type: int32
+                      default: '0'
+                    gigabytes:
+                      type: int32
+                      default: '0'
+                      units: GB
+                    snapshots:
+                      type: int16
+                      default: '0'
+                    volumes:
+                      type: int16
+                      default: '0'
+                available:
+                  description: Conceptual container that should be extended
+                  config: false
+                  uses: {}
+                  leaf:
+                    cores:
+                      type: int16
+                      default: '0'
+                    ram:
+                      type: int32
+                      default: '0'
+                      units: MB
+                    instances:
+                      type: int16
+                      default: '0'
+                    networks:
+                      type: int16
+                      default: '0'
+                    ports:
+                      type: int16
+                      default: '0'
+                    routers:
+                      type: int16
+                      default: '0'
+                    subnets:
+                      type: int16
+                      default: '0'
+                    addresses:
+                      type: int32
+                      default: '0'
+                    gigabytes:
+                      type: int32
+                      default: '0'
+                      units: GB
+                    snapshots:
+                      type: int16
+                      default: '0'
+                    volumes:
+                      type: int16
+                      default: '0'
+      rpc:
+        create-reservation:
+          if-feature: reservation-service
+          description: Make a request to the reservation system to reserve resources
+          input:
+            uses: {}
+            leaf:
+              zone:
+                description: Optional identifier to an Availability Zone
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:AvailabilityZone'
+              start:
+                type: 'yang:date-and-time'
+              end:
+                type: 'yang:date-and-time'
+            container:
+              capacity:
+                uses: {}
+                leaf:
+                  cores:
+                    type: int16
+                    default: '0'
+                  ram:
+                    type: int32
+                    default: '0'
+                    units: MB
+                  instances:
+                    type: int16
+                    default: '0'
+                  networks:
+                    type: int16
+                    default: '0'
+                  ports:
+                    type: int16
+                    default: '0'
+                  routers:
+                    type: int16
+                    default: '0'
+                  subnets:
+                    type: int16
+                    default: '0'
+                  addresses:
+                    type: int32
+                    default: '0'
+                  gigabytes:
+                    type: int32
+                    default: '0'
+                    units: GB
+                  snapshots:
+                    type: int16
+                    default: '0'
+                  volumes:
+                    type: int16
+                    default: '0'
+            leaf-list:
+              elements:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceElement'
+                    require-instance: true
+                description: |-
+                  Reference to a list of 'pre-existing' resource elements that are
+                  required for fulfillment of the resource-usage-request.
+
+                  It can contain any instance derived from ResourceElement,
+                  such as ServerInstances or even other
+                  ResourceReservations. If the resource-usage-request is
+                  accepted, the ResourceElement(s) listed here will be placed
+                  into 'protected' mode as to prevent accidental removal.
+
+                  If any of these resource elements become 'unavailable' due to
+                  environmental or administrative activity, a notification will
+                  be issued informing of the issue.
+          output:
+            leaf:
+              reservation-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceReservation
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+          status: unavailable
+        update-reservation:
+          description: Update reservation details for an existing reservation
+          input:
+            leaf:
+              reservation-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceReservation
+                    require-instance: true
+                mandatory: true
+              zone:
+                description: Optional identifier to an Availability Zone
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:AvailabilityZone'
+              start:
+                type: 'yang:date-and-time'
+              end:
+                type: 'yang:date-and-time'
+            uses: {}
+            container:
+              capacity:
+                uses: {}
+                leaf:
+                  cores:
+                    type: int16
+                    default: '0'
+                  ram:
+                    type: int32
+                    default: '0'
+                    units: MB
+                  instances:
+                    type: int16
+                    default: '0'
+                  networks:
+                    type: int16
+                    default: '0'
+                  ports:
+                    type: int16
+                    default: '0'
+                  routers:
+                    type: int16
+                    default: '0'
+                  subnets:
+                    type: int16
+                    default: '0'
+                  addresses:
+                    type: int32
+                    default: '0'
+                  gigabytes:
+                    type: int32
+                    default: '0'
+                    units: GB
+                  snapshots:
+                    type: int16
+                    default: '0'
+                  volumes:
+                    type: int16
+                    default: '0'
+            leaf-list:
+              elements:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceElement'
+                    require-instance: true
+                description: |-
+                  Reference to a list of 'pre-existing' resource elements that are
+                  required for fulfillment of the resource-usage-request.
+
+                  It can contain any instance derived from ResourceElement,
+                  such as ServerInstances or even other
+                  ResourceReservations. If the resource-usage-request is
+                  accepted, the ResourceElement(s) listed here will be placed
+                  into 'protected' mode as to prevent accidental removal.
+
+                  If any of these resource elements become 'unavailable' due to
+                  environmental or administrative activity, a notification will
+                  be issued informing of the issue.
+          output:
+            leaf:
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+        cancel-reservation:
+          description: Cancel the reservation and be a good steward
+          input:
+            leaf:
+              reservation-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceReservation
+                mandatory: true
+          output:
+            leaf:
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+        query-reservation:
+          if-feature: reservation-service
+          description: Query the reservation system to return matching reservation(s)
+          input:
+            leaf:
+              zone:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:AvailabilityZone'
+              show-utilization:
+                type: boolean
+                default: 'true'
+            uses: {}
+            leaf-list:
+              without:
+                description: Excludes specified collection identifiers from the result
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceCollection
+            container:
+              elements:
+                leaf-list:
+                  some:
+                    description: Query for ResourceCollection(s) that contain some or more of these element(s)
+                    type:
+                      instance-identifier:
+                        'ct:instance-type': 'nfvi:ResourceElement'
+                  every:
+                    description: Query for ResourceCollection(s) that contain all of these element(s)
+                    type:
+                      instance-identifier:
+                        'ct:instance-type': 'nfvi:ResourceElement'
+              window:
+                description: Matches entries that are within the specified start/end time window
+                leaf:
+                  start:
+                    type: 'yang:date-and-time'
+                  end:
+                    type: 'yang:date-and-time'
+                  scope:
+                    type:
+                      enumeration:
+                        enum:
+                          exclusive:
+                            description: Matches entries that start AND end within the window
+                            value: 0
+                          inclusive:
+                            description: Matches entries that start OR end within the window
+                            value: 1
+                    default: inclusive
+          output:
+            leaf-list:
+              reservations:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceReservation
+            list:
+              utilization:
+                key: timestamp
+                leaf:
+                  timestamp:
+                    type: 'yang:date-and-time'
+                  count:
+                    type: int16
+                container:
+                  capacity:
+                    uses: {}
+                    leaf:
+                      cores:
+                        type: int16
+                        default: '0'
+                      ram:
+                        type: int32
+                        default: '0'
+                        units: MB
+                      instances:
+                        type: int16
+                        default: '0'
+                      networks:
+                        type: int16
+                        default: '0'
+                      ports:
+                        type: int16
+                        default: '0'
+                      routers:
+                        type: int16
+                        default: '0'
+                      subnets:
+                        type: int16
+                        default: '0'
+                      addresses:
+                        type: int32
+                        default: '0'
+                      gigabytes:
+                        type: int32
+                        default: '0'
+                        units: GB
+                      snapshots:
+                        type: int16
+                        default: '0'
+                      volumes:
+                        type: int16
+                        default: '0'
+          status: unavailable
+        increase-capacity:
+          description: Increase total capacity for the reservation system between a window in time
+          input:
+            leaf:
+              source:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceContainer'
+              start:
+                type: 'yang:date-and-time'
+              end:
+                type: 'yang:date-and-time'
+            container:
+              capacity:
+                uses: {}
+                leaf:
+                  cores:
+                    type: int16
+                    default: '0'
+                  ram:
+                    type: int32
+                    default: '0'
+                    units: MB
+                  instances:
+                    type: int16
+                    default: '0'
+                  networks:
+                    type: int16
+                    default: '0'
+                  ports:
+                    type: int16
+                    default: '0'
+                  routers:
+                    type: int16
+                    default: '0'
+                  subnets:
+                    type: int16
+                    default: '0'
+                  addresses:
+                    type: int32
+                    default: '0'
+                  gigabytes:
+                    type: int32
+                    default: '0'
+                    units: GB
+                  snapshots:
+                    type: int16
+                    default: '0'
+                  volumes:
+                    type: int16
+                    default: '0'
+            leaf-list:
+              elements:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceElement'
+                    require-instance: true
+          output:
+            leaf:
+              pool-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourcePool
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+        decrease-capacity:
+          description: Decrease total capacity for the reservation system between a window in time
+          input:
+            leaf:
+              source:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceContainer'
+              start:
+                type: 'yang:date-and-time'
+              end:
+                type: 'yang:date-and-time'
+            container:
+              capacity:
+                uses: {}
+                leaf:
+                  cores:
+                    type: int16
+                    default: '0'
+                  ram:
+                    type: int32
+                    default: '0'
+                    units: MB
+                  instances:
+                    type: int16
+                    default: '0'
+                  networks:
+                    type: int16
+                    default: '0'
+                  ports:
+                    type: int16
+                    default: '0'
+                  routers:
+                    type: int16
+                    default: '0'
+                  subnets:
+                    type: int16
+                    default: '0'
+                  addresses:
+                    type: int32
+                    default: '0'
+                  gigabytes:
+                    type: int32
+                    default: '0'
+                    units: GB
+                  snapshots:
+                    type: int16
+                    default: '0'
+                  volumes:
+                    type: int16
+                    default: '0'
+            leaf-list:
+              elements:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceElement'
+                    require-instance: true
+          output:
+            leaf:
+              pool-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourcePool
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+        query-capacity:
+          description: Check available capacity information about a specified resource collection
+          input:
+            leaf:
+              capacity:
+                type:
+                  enumeration:
+                    enum:
+                      total:
+                        value: 0
+                      reserved:
+                        value: 1
+                      usage:
+                        value: 2
+                      available:
+                        value: 3
+                default: available
+              zone:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:AvailabilityZone'
+              show-utilization:
+                type: boolean
+                default: 'true'
+            uses: {}
+            leaf-list:
+              without:
+                description: Excludes specified collection identifiers from the result
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceCollection
+            container:
+              elements:
+                leaf-list:
+                  some:
+                    description: Query for ResourceCollection(s) that contain some or more of these element(s)
+                    type:
+                      instance-identifier:
+                        'ct:instance-type': 'nfvi:ResourceElement'
+                  every:
+                    description: Query for ResourceCollection(s) that contain all of these element(s)
+                    type:
+                      instance-identifier:
+                        'ct:instance-type': 'nfvi:ResourceElement'
+              window:
+                description: Matches entries that are within the specified start/end time window
+                leaf:
+                  start:
+                    type: 'yang:date-and-time'
+                  end:
+                    type: 'yang:date-and-time'
+                  scope:
+                    type:
+                      enumeration:
+                        enum:
+                          exclusive:
+                            description: Matches entries that start AND end within the window
+                            value: 0
+                          inclusive:
+                            description: Matches entries that start OR end within the window
+                            value: 1
+                    default: inclusive
+          output:
+            leaf-list:
+              collections:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceCollection
+            list:
+              utilization:
+                key: timestamp
+                leaf:
+                  timestamp:
+                    type: 'yang:date-and-time'
+                  count:
+                    type: int16
+                container:
+                  capacity:
+                    uses: {}
+                    leaf:
+                      cores:
+                        type: int16
+                        default: '0'
+                      ram:
+                        type: int32
+                        default: '0'
+                        units: MB
+                      instances:
+                        type: int16
+                        default: '0'
+                      networks:
+                        type: int16
+                        default: '0'
+                      ports:
+                        type: int16
+                        default: '0'
+                      routers:
+                        type: int16
+                        default: '0'
+                      subnets:
+                        type: int16
+                        default: '0'
+                      addresses:
+                        type: int32
+                        default: '0'
+                      gigabytes:
+                        type: int32
+                        default: '0'
+                        units: GB
+                      snapshots:
+                        type: int16
+                        default: '0'
+                      volumes:
+                        type: int16
+                        default: '0'
+        create-instance:
+          description: Create an instance of specified resource(s) utilizing capacity from the pool
+          input:
+            leaf:
+              provider-id:
+                if-feature: multi-provider
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceProvider
+                    require-instance: true
+                status: unavailable
+              name:
+                type: string
+                mandatory: true
+              image:
+                type:
+                  union:
+                    type:
+                      'yang:uuid': null
+                      'inet:uri': null
+                mandatory: true
+              flavor:
+                type:
+                  union:
+                    type:
+                      'yang:uuid': null
+                      'inet:uri': null
+                mandatory: true
+              reservation-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceReservation
+                    require-instance: true
+          output:
+            leaf:
+              instance-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceAllocation
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+        destroy-instance:
+          description: Destroy an instance of resource utilization and release it back to the pool
+          input:
+            leaf:
+              instance-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceAllocation
+                    require-instance: true
+          output:
+            leaf:
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+        add-provider:
+          description: Register a new resource provider into reservation system
+          input:
+            leaf:
+              provider-type:
+                description: Select a specific resource provider type
+                mandatory: true
+                type:
+                  enumeration:
+                    enum:
+                      openstack:
+                        value: 0
+                      hp:
+                        value: 1
+                      rackspace:
+                        value: 2
+                      amazon:
+                        status: planned
+                        value: 3
+                      joyent:
+                        status: planned
+                        value: 4
+                      azure:
+                        status: planned
+                        value: 5
+                default: openstack
+              strategy:
+                type:
+                  enumeration:
+                    enum:
+                      oauth:
+                        value: 0
+                      keystone:
+                        value: 1
+                default: keystone
+              endpoint:
+                type: 'inet:uri'
+                description: The target endpoint for authentication
+                mandatory: true
+                default: 'http://localhost:5000/v2.0'
+              username:
+                type: string
+                mandatory: true
+              password:
+                type: 'acm:password'
+                mandatory: true
+            uses: {}
+            container:
+              tenant:
+                leaf:
+                  id:
+                    type: string
+                  name:
+                    type: string
+          output:
+            leaf:
+              provider-id:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceProvider
+              result:
+                type:
+                  enumeration:
+                    enum:
+                      ok:
+                        value: 0
+                      conflict:
+                        value: 1
+                      error:
+                        value: 2
+              message:
+                type: string
+      notification:
+        reservation-event: null
+        capacity-event: null
+        allocation-event: null
+dependencies:
+  access-control-models:
+    module:
+      access-control-models:
+        prefix: acm
+        namespace: 'urn:opnfv:promise:acm'
+        import:
+          complex-types:
+            prefix: ct
+          ietf-yang-types:
+            prefix: yang
+          ietf-inet-types:
+            prefix: inet
+        typedef:
+          password:
+            type:
+              string:
+                length: 1..255
+        grouping:
+          access-credentials:
+            leaf:
+              strategy:
+                type:
+                  enumeration:
+                    enum:
+                      oauth:
+                        value: 0
+                      keystone:
+                        value: 1
+                default: oauth
+              endpoint:
+                type: 'inet:uri'
+                description: The target endpoint for authentication
+                mandatory: true
+              username:
+                type: string
+                mandatory: true
+              password:
+                type: 'acm:password'
+                mandatory: true
+        'ct:complex-type':
+          Identity:
+            'ct:abstract': 'true'
+            description: Identity represents an administrative access model entity
+            key: id
+            leaf:
+              id:
+                type: 'yang:uuid'
+                mandatory: true
+              name:
+                type: string
+                mandatory: true
+              description:
+                type: string
+              enabled:
+                type: boolean
+                default: 'true'
+          User:
+            'ct:extends': Identity
+            leaf:
+              credential:
+                type: string
+                mandatory: true
+              domain:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': Domain
+            container:
+              contact:
+                leaf:
+                  fullName:
+                    type: string
+                  email:
+                    type: string
+            leaf-list:
+              groups:
+                type:
+                  instance-identifer:
+                    'ct:instance-type': Group
+          Group:
+            'ct:extends': Identity
+            leaf-list:
+              users:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': User
+            leaf:
+              domain:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': Domain
+          Domain:
+            'ct:extends': Identity
+            description: |-
+              Domain represent a distinct administrative domain across
+              collection of users and groups.
+            'ct:instance-list':
+              users:
+                'ct:instance-type': User
+              groups:
+                'ct:instance-type': Group
+        rpc:
+          create-user: null
+          remove-user: null
+          create-group: null
+          remove-group: null
+  nfv-infrastructure:
+    module:
+      nfv-infrastructure:
+        namespace: 'urn:opnfv:promise:nfv:infrastructure'
+        prefix: nfvi
+        import:
+          access-control-models:
+            prefix: acm
+          ietf-inet-types:
+            prefix: inet
+          ietf-yang-types:
+            prefix: yang
+          complex-types:
+            prefix: ct
+        description: |-
+          NFV Infrastructure Data Models with complex types and typed instance
+          identifiers representing the various ResourceElements available
+          in the infrastructure across compute, network, and storage.
+        revision:
+          '2015-10-13':
+            description: Introduce capacity and element collection into NFVI models
+          '2015-08-07':
+            description: |-
+              This YANG module is modeled using 'yangforge' which natively provides
+              complex types and typed instance identifiers.  This module
+              provides various collections of resource management data models
+              for instance based management
+        identity:
+          manager:
+            description: used by specific modules implementing manager role for NFVI
+        grouping:
+          compute-capacity:
+            leaf:
+              cores:
+                type: int16
+                default: '0'
+              ram:
+                type: int32
+                default: '0'
+                units: MB
+              instances:
+                type: int16
+                default: '0'
+          network-capacity:
+            leaf:
+              networks:
+                type: int16
+                default: '0'
+              ports:
+                type: int16
+                default: '0'
+              routers:
+                type: int16
+                default: '0'
+              subnets:
+                type: int16
+                default: '0'
+              addresses:
+                type: int32
+                default: '0'
+          storage-capacity:
+            leaf:
+              gigabytes:
+                type: int32
+                default: '0'
+                units: GB
+              snapshots:
+                type: int16
+                default: '0'
+              volumes:
+                type: int16
+                default: '0'
+          resource-capacity:
+            uses: {}
+            leaf:
+              cores:
+                type: int16
+                default: '0'
+              ram:
+                type: int32
+                default: '0'
+                units: MB
+              instances:
+                type: int16
+                default: '0'
+              networks:
+                type: int16
+                default: '0'
+              ports:
+                type: int16
+                default: '0'
+              routers:
+                type: int16
+                default: '0'
+              subnets:
+                type: int16
+                default: '0'
+              addresses:
+                type: int32
+                default: '0'
+              gigabytes:
+                type: int32
+                default: '0'
+                units: GB
+              snapshots:
+                type: int16
+                default: '0'
+              volumes:
+                type: int16
+                default: '0'
+          resource-collection:
+            description: |-
+              Information model capturing parameters for describing a collection of
+              resource capacity and resource elements
+            container:
+              capacity:
+                uses: {}
+                leaf:
+                  cores:
+                    type: int16
+                    default: '0'
+                  ram:
+                    type: int32
+                    default: '0'
+                    units: MB
+                  instances:
+                    type: int16
+                    default: '0'
+                  networks:
+                    type: int16
+                    default: '0'
+                  ports:
+                    type: int16
+                    default: '0'
+                  routers:
+                    type: int16
+                    default: '0'
+                  subnets:
+                    type: int16
+                    default: '0'
+                  addresses:
+                    type: int32
+                    default: '0'
+                  gigabytes:
+                    type: int32
+                    default: '0'
+                    units: GB
+                  snapshots:
+                    type: int16
+                    default: '0'
+                  volumes:
+                    type: int16
+                    default: '0'
+            leaf-list:
+              elements:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceElement'
+                    require-instance: true
+          resource-stack:
+            description: |-
+              Information model describing a NFVI resource stack comprising of
+              various resource elements across compute, network, and storage
+            'ct:instance-list':
+              hosts:
+                'ct:instance-type': 'nfvi:PhysicalHost'
+              hypervisors:
+                'ct:instance-type': 'nfvi:Hypervisor'
+            container:
+              compute:
+                description: Contains compute related resources
+                'ct:instance-list':
+                  servers:
+                    'ct:instance-type': 'nfvi:ServerInstance'
+                  images:
+                    'ct:instance-type': 'nfvi:VirtualMachineImage'
+                  flavors:
+                    'ct:instance-type': 'nfvi:ComputeFlavor'
+              network:
+                description: Contains networking related resources
+                'ct:instance-list':
+                  networks:
+                    'ct:instance-type': 'nfvi:Network'
+                  subnets:
+                    'ct:instance-type': 'nfvi:SubNetwork'
+                  ports:
+                    'ct:instance-type': 'nfvi:SwitchPort'
+        'ct:complex-type':
+          ResourceElement:
+            'ct:abstract': 'true'
+            key: id
+            leaf:
+              id:
+                type: 'yang:uuid'
+                mandatory: true
+              name:
+                type: string
+              enabled:
+                type: boolean
+                default: 'true'
+              protected:
+                type: boolean
+                default: 'false'
+              owner:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'acm:Identity'
+              visibility:
+                description: "Specify visibility level available from the perspective of 'owner'"
+                type:
+                  enumeration:
+                    enum:
+                      public:
+                        value: 0
+                      domain:
+                        value: 1
+                      project:
+                        value: 2
+                      group:
+                        value: 3
+                      user:
+                        value: 4
+                default: user
+            leaf-list:
+              tags:
+                type: string
+              members:
+                description: Optionally share with explicit list of members of AccessIdentity complex-type
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'acm:Identity'
+          ResourceInstance:
+            'ct:extends': ResourceElement
+            'ct:abstract': 'true'
+            leaf:
+              status:
+                type:
+                  enumeration:
+                    enum:
+                      active:
+                        value: 0
+                      inactive:
+                        value: 1
+                      pending:
+                        value: 2
+              progress:
+                type:
+                  uint8:
+                    range: 0..100
+                default: '0'
+          ResourceContainer:
+            'ct:extends': ResourceInstance
+            'ct:abstract': 'true'
+            description: |-
+              An abstract resource instance which contains a collection of capacity
+              and elements.
+            container:
+              capacity:
+                uses: {}
+                leaf:
+                  cores:
+                    type: int16
+                    default: '0'
+                  ram:
+                    type: int32
+                    default: '0'
+                    units: MB
+                  instances:
+                    type: int16
+                    default: '0'
+                  networks:
+                    type: int16
+                    default: '0'
+                  ports:
+                    type: int16
+                    default: '0'
+                  routers:
+                    type: int16
+                    default: '0'
+                  subnets:
+                    type: int16
+                    default: '0'
+                  addresses:
+                    type: int32
+                    default: '0'
+                  gigabytes:
+                    type: int32
+                    default: '0'
+                    units: GB
+                  snapshots:
+                    type: int16
+                    default: '0'
+                  volumes:
+                    type: int16
+                    default: '0'
+            leaf-list:
+              elements:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': 'nfvi:ResourceElement'
+                    require-instance: true
+          AvailabilityZone:
+            'ct:extends': ResourceElement
+          PhysicalHost:
+            'ct:extends': ResourceElement
+            leaf:
+              type:
+                type: string
+              version:
+                type: string
+              cpu:
+                type: uint8
+              workload:
+                type: uint8
+                default: '0'
+              uptime:
+                type: string
+            container:
+              ram:
+                leaf:
+                  total:
+                    type: uint32
+                    units: MB
+                  used:
+                    type: uint32
+                    units: MB
+                  free:
+                    type: uint32
+                    units: MB
+              disk:
+                leaf:
+                  total:
+                    type: uint32
+                    units: GB
+                  used:
+                    type: uint32
+                    units: GB
+                  free:
+                    type: uint32
+                    units: GB
+            leaf-list:
+              hypervisors:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': Hypervisor
+          Hypervisor:
+            'ct:extends': PhysicalHost
+            leaf:
+              host:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': PhysicalHost
+                mandatory: true
+            container:
+              vcpu:
+                leaf:
+                  total:
+                    type: uint16
+                  used:
+                    type: uint16
+                  free:
+                    type: uint16
+            leaf-list:
+              servers:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ServerInstance
+          ComputeElement:
+            'ct:extends': ResourceElement
+            'ct:abstract': 'true'
+            container:
+              constraint:
+                leaf:
+                  disk:
+                    type: uint32
+                    units: GB
+                    default: '0'
+                  ram:
+                    type: uint32
+                    units: MB
+                    default: '0'
+                  vcpu:
+                    type: uint16
+                    default: '0'
+            leaf-list:
+              instances:
+                description: State info about instances currently using this resource element
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ResourceInstance
+                config: false
+          VirtualMachineImage:
+            'ct:extends': ComputeElement
+            container:
+              data:
+                leaf:
+                  checksum:
+                    type: string
+                    mandatory: true
+                  size:
+                    type: uint32
+                    units: Bytes
+                    mandatory: true
+                  content:
+                    description: "should be a 'private' property so only direct access retrieves content"
+                    type: binary
+                container:
+                  format:
+                    leaf:
+                      container:
+                        type:
+                          enumeration:
+                            enum:
+                              ami:
+                                value: 0
+                              ari:
+                                value: 1
+                              aki:
+                                value: 2
+                              bare:
+                                value: 3
+                              ovf:
+                                value: 4
+                        default: bare
+                      disk:
+                        type:
+                          enumeration:
+                            enum:
+                              ami:
+                                value: 0
+                              ari:
+                                value: 1
+                              aki:
+                                value: 2
+                              vhd:
+                                value: 3
+                              vmdk:
+                                value: 4
+                              raw:
+                                value: 5
+                              qcow2:
+                                value: 6
+                              vdi:
+                                value: 7
+                              iso:
+                                value: 8
+          ComputeFlavor:
+            'ct:extends': ResourceElement
+            leaf:
+              disk:
+                type: uint32
+                units: GB
+                default: '0'
+              ram:
+                type: uint32
+                units: MB
+                default: '0'
+              vcpus:
+                type: uint16
+                default: '0'
+          ServerInstance:
+            'ct:extends': ResourceInstance
+            leaf:
+              flavor:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': ComputeFlavor
+                mandatory: true
+              image:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': VirtualMachineImage
+                mandatory: true
+              host:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': PhysicalHost
+            leaf-list:
+              connections:
+                description: |-
+                  References to collection of NetworkingElement class objects such as
+                  Network, Subnet, Port, Router that this ServerInstance is
+                  connected with.
+                type:
+                  instance-identifier:
+                    'ct:instance-type': NetworkElement
+          NetworkElement:
+            'ct:extends': ResourceElement
+            'ct:abstract': 'true'
+          Network:
+            'ct:extends': NetworkElement
+            leaf-list:
+              subnets:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': SubNetwork
+          SubNetwork:
+            'ct:extends': NetworkElement
+            leaf:
+              network:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': Network
+            leaf-list:
+              nameservers:
+                type: string
+            container:
+              dhcp:
+                leaf:
+                  enabled:
+                    type: boolean
+                list:
+                  pools:
+                    leaf:
+                      start:
+                        type: 'inet:ip-address'
+                      end:
+                        type: 'inet:ip-address'
+          SwitchPort:
+            'ct:extends': NetworkElement
+            leaf:
+              subnet:
+                type:
+                  instance-identifier:
+                    'ct:instance-type': SubNetwork
+extension:
+  module:
+    argument: name
+    include: 0..n
+    prefix: 0..1
+    anyxml: 0..n
+    augment: 0..n
+    choice: 0..n
+    contact: 0..1
+    container: 0..n
+    description: 0..1
+    deviation: 0..n
+    extension: 0..n
+    feature: 0..n
+    grouping: 0..n
+    identity: 0..n
+    import: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    namespace: 0..1
+    notification: 0..n
+    organization: 0..1
+    reference: 0..1
+    revision: 0..n
+    rpc: 0..n
+    typedef: 0..n
+    uses: 0..n
+    yang-version: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        var changes, match, ref, synth, target;
+        synth = this.require('data-synth');
+        ref = params.augment;
+        for (target in ref) {
+          changes = ref[target];
+          match = this.locate(ctx, target);
+          if (match == null) {
+            continue;
+          }
+          synth.copy(match, changes);
+        }
+        return delete this.source[params.prefix];
+      }
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children, ctx, self) {
+        return (self.origin.construct.apply(this, arguments)).merge({
+          models: this.resolve('complex-type')
+        });
+      }
+    complex-type: 0..n
+    instance: 0..n
+    instance-list: 0..n
+    origin:
+      argument: name
+      include: 0..n
+      prefix: 0..1
+      anyxml: 0..n
+      augment: 0..n
+      choice: 0..n
+      contact: 0..1
+      container: 0..n
+      description: 0..1
+      deviation: 0..n
+      extension: 0..n
+      feature: 0..n
+      grouping: 0..n
+      identity: 0..n
+      import: 0..n
+      leaf: 0..n
+      leaf-list: 0..n
+      list: 0..n
+      namespace: 0..1
+      notification: 0..n
+      organization: 0..1
+      reference: 0..1
+      revision: 0..n
+      rpc: 0..n
+      typedef: 0..n
+      uses: 0..n
+      yang-version: 0..1
+      preprocess: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, ctx) {
+          var changes, match, ref, synth, target;
+          synth = this.require('data-synth');
+          ref = params.augment;
+          for (target in ref) {
+            changes = ref[target];
+            match = this.locate(ctx, target);
+            if (match == null) {
+              continue;
+            }
+            synth.copy(match, changes);
+          }
+          return delete this.source[params.prefix];
+        }
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children) {
+          var k, m, modules, ref, synth, v;
+          synth = this.require('data-synth');
+          modules = {};
+          ref = params["import"];
+          for (k in ref) {
+            v = ref[k];
+            modules[k] = children[k];
+            delete children[k];
+          }
+          m = (synth.Store(params, function() {
+            return this.set({
+              name: arg,
+              modules: modules
+            });
+          })).bind(children);
+          this.define('module', arg, m);
+          return m;
+        }
+  prefix:
+    argument: value
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return this.source[arg] = this.source;
+      }
+  include:
+    argument: module
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        var k, m, ref, ref1, ref2, results, v;
+        m = this.preprocess(this.resolve('dependencies', arg));
+        ref = m.extension;
+        for (k in ref) {
+          v = ref[k];
+          this.define('extension', k, v);
+        }
+        ref1 = m.typedef;
+        for (k in ref1) {
+          v = ref1[k];
+          this.define('typedef', k, v);
+        }
+        ref2 = m.schema;
+        results = [];
+        for (k in ref2) {
+          v = ref2[k];
+          results.push(ctx[k] = v);
+        }
+        return results;
+      }
+    revision-date: 0..1
+  augment:
+    anyxml: 0..n
+    case: 0..n
+    choice: 0..n
+    container: 0..n
+    description: 0..1
+    if-feature: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    reference: 0..1
+    status: 0..1
+    uses: 0..n
+    when: 0..1
+    argument: target-node
+  belongs-to:
+    prefix: 1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return this.source[params.prefix] = this.source;
+      }
+    argument: module
+  bit:
+    description: 0..1
+    reference: 0..1
+    status: 0..1
+    position: 0..1
+    argument: name
+  case:
+    anyxml: 0..n
+    choice: 0..n
+    container: 0..n
+    description: 0..1
+    if-feature: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    reference: 0..1
+    status: 0..1
+    uses: 0..n
+    when: 0..1
+    argument: name
+  choice:
+    anyxml: 0..n
+    case: 0..n
+    config: 0..1
+    container: 0..n
+    default: 0..1
+    description: 0..1
+    if-feature: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    mandatory: 0..1
+    reference: 0..1
+    status: 0..1
+    when: 0..1
+    argument: condition
+  config:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, p, ctx) {
+        return ctx.config = arg === true || arg === 'true';
+      }
+    argument: value
+  container:
+    anyxml: 0..n
+    choice: 0..n
+    config: 0..1
+    container: 0..n
+    description: 0..1
+    grouping: 0..n
+    if-feature: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    must: 0..n
+    presence: 0..1
+    reference: 0..1
+    status: 0..1
+    typedef: 0..n
+    uses: 0..n
+    when: 0..1
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children) {
+        var synth;
+        synth = this.require('data-synth');
+        return (synth.Object(params)).bind(children);
+      }
+    argument: name
+    instance: 0..n
+    instance-list: 0..n
+    origin:
+      anyxml: 0..n
+      choice: 0..n
+      config: 0..1
+      container: 0..n
+      description: 0..1
+      grouping: 0..n
+      if-feature: 0..n
+      leaf: 0..n
+      leaf-list: 0..n
+      list: 0..n
+      must: 0..n
+      presence: 0..1
+      reference: 0..1
+      status: 0..1
+      typedef: 0..n
+      uses: 0..n
+      when: 0..1
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children) {
+          var synth;
+          synth = this.require('data-synth');
+          return (synth.Object(params)).bind(children);
+        }
+      argument: name
+  deviate:
+    config: 0..1
+    default: 0..1
+    mandatory: 0..1
+    max-elements: 0..1
+    min-elements: 0..1
+    must: 0..n
+    type: 0..1
+    unique: 0..1
+    units: 0..1
+    argument: value
+  deviation:
+    description: 0..1
+    deviate: 1..n
+    reference: 0..1
+    argument: target-node
+  enum:
+    description: 0..1
+    reference: 0..1
+    status: 0..1
+    value: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        if (params.value == null) {
+          if (this.enumValue == null) {
+            this.enumValue = 0;
+          }
+          params.value = this.enumValue++;
+        } else {
+          params.value = Number(params.value);
+          this.enumValue = params.value + 1;
+        }
+        return ctx["enum"][arg] = params;
+      }
+    argument: name
+  feature:
+    description: 0..1
+    if-feature: 0..n
+    reference: 0..1
+    status: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        if (params.status === 'unavailable') {
+          console.warn("feature " + arg + " is unavailable");
+          if (typeof ctx.feature === 'object') {
+            return delete ctx.feature[arg];
+          } else {
+            return delete ctx.feature;
+          }
+        }
+      }
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children) {
+        var feature;
+        feature = this.resolve('feature', arg);
+        return null;
+      }
+    argument: name
+  grouping:
+    anyxml: 0..n
+    choice: 0..n
+    container: 0..n
+    description: 0..1
+    grouping: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    reference: 0..1
+    status: 0..1
+    typedef: 0..n
+    uses: 0..n
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params) {
+        return this.define('grouping', arg, params);
+      }
+    argument: name
+    instance: 0..n
+    instance-list: 0..n
+    origin:
+      anyxml: 0..n
+      choice: 0..n
+      container: 0..n
+      description: 0..1
+      grouping: 0..n
+      leaf: 0..n
+      leaf-list: 0..n
+      list: 0..n
+      reference: 0..1
+      status: 0..1
+      typedef: 0..n
+      uses: 0..n
+      preprocess: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params) {
+          return this.define('grouping', arg, params);
+        }
+      argument: name
+  identity:
+    base: 0..1
+    description: 0..1
+    reference: 0..1
+    status: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params) {
+        return this.define('identity', arg, params);
+      }
+    argument: name
+  if-feature:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        if ((this.resolve('feature', arg)) == null) {
+          return ctx.status = 'unavailable';
+        }
+      }
+    argument: name
+  import:
+    prefix: 1
+    revision-date: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        var copy, k, m, original, ref, ref1, rev, schema, source, synth, v;
+        synth = this.require('data-synth');
+        schema = this.resolve('dependencies', arg, false);
+        if (schema == null) {
+          console.warn("no explicit dependency for " + arg + " defined, searching local filesystem");
+          schema = this.parse("!yang " + arg + ".yang", this.source);
+          if (schema != null) {
+            this.define('dependencies', arg, schema);
+            source = this.source.parent;
+            while ((source.parent != null) && source.parent.name !== 'yangforge') {
+              source = source.parent;
+            }
+            if (source.dependencies == null) {
+              source.dependencies = {};
+            }
+            source.dependencies[arg] = schema;
+          }
+        }
+        m = this.preprocess(schema);
+        if (m == null) {
+          throw this.error("unable to resolve '" + arg + "' in dependencies", 'import');
+        }
+        rev = params['revision-date'];
+        if ((rev != null) && !(rev in m.revision)) {
+          throw this.error("requested " + rev + " not available in " + arg, 'import');
+        }
+        ref = m.extension;
+        for (k in ref) {
+          v = ref[k];
+          if (!(v.override === true)) {
+            continue;
+          }
+          original = this.resolve('extension', k);
+          copy = synth.copy({}, v);
+          copy.origin = synth.copy({}, (ref1 = original.origin) != null ? ref1 : original);
+          delete copy.override;
+          this.define('extension', k, copy);
+        }
+        return this.source[params.prefix] = m;
+      }
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children, ctx) {
+        return this.compile(this.source[params.prefix], this.source);
+      }
+    argument: module
+  input:
+    anyxml: 0..n
+    choice: 0..n
+    container: 0..n
+    grouping: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    typedef: 0..n
+    uses: 0..n
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children) {
+        var synth;
+        synth = this.require('data-synth');
+        return (synth.Object(params)).bind(children);
+      }
+    instance: 0..n
+    instance-list: 0..n
+    origin:
+      anyxml: 0..n
+      choice: 0..n
+      container: 0..n
+      grouping: 0..n
+      leaf: 0..n
+      leaf-list: 0..n
+      list: 0..n
+      typedef: 0..n
+      uses: 0..n
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children) {
+          var synth;
+          synth = this.require('data-synth');
+          return (synth.Object(params)).bind(children);
+        }
+  leaf:
+    config: 0..1
+    default: 0..1
+    description: 0..1
+    if-feature: 0..n
+    mandatory: 0..1
+    must: 0..n
+    reference: 0..1
+    status: 0..1
+    type: 0..1
+    units: 0..1
+    when: 0..1
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children, ctx, self) {
+        var synth;
+        synth = this.require('data-synth');
+        if (params.type['instance-identifier'] != null) {
+          return synth.BelongsTo(params, function() {
+            return this.set({
+              model: children.type
+            });
+          });
+        } else {
+          return self.origin.construct.apply(this, arguments);
+        }
+      }
+    argument: name
+    origin:
+      config: 0..1
+      default: 0..1
+      description: 0..1
+      if-feature: 0..n
+      mandatory: 0..1
+      must: 0..n
+      reference: 0..1
+      status: 0..1
+      type: 0..1
+      units: 0..1
+      when: 0..1
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children) {
+          var synth;
+          synth = this.require('data-synth');
+          return synth.Property(params, function() {
+            if (children.type != null) {
+              return this.set({
+                type: children.type
+              });
+            }
+          });
+        }
+      argument: name
+  leaf-list:
+    config: 0..1
+    description: 0..1
+    if-feature: 0..n
+    max-elements: 0..1
+    min-elements: 0..1
+    must: 0..n
+    ordered-by: 0..1
+    reference: 0..1
+    status: 0..1
+    type: 0..1
+    units: 0..1
+    when: 0..1
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children, ctx, self) {
+        var synth;
+        synth = this.require('data-synth');
+        if (params.type['instance-identifier'] != null) {
+          return synth.HasMany(params, function() {
+            return this.set({
+              model: children.type
+            });
+          });
+        } else {
+          return self.origin.construct.apply(this, arguments);
+        }
+      }
+    argument: name
+    origin:
+      config: 0..1
+      description: 0..1
+      if-feature: 0..n
+      max-elements: 0..1
+      min-elements: 0..1
+      must: 0..n
+      ordered-by: 0..1
+      reference: 0..1
+      status: 0..1
+      type: 0..1
+      units: 0..1
+      when: 0..1
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children) {
+          var synth;
+          synth = this.require('data-synth');
+          return synth.List(params, function() {
+            if (children.type != null) {
+              return this.set({
+                type: children.type
+              });
+            }
+          });
+        }
+      argument: name
+  list:
+    anyxml: 0..n
+    choice: 0..n
+    config: 0..1
+    container: 0..n
+    description: 0..1
+    grouping: 0..n
+    if-feature: 0..n
+    key: 0..1
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    max-elements: 0..1
+    min-elements: 0..1
+    must: 0..n
+    ordered-by: 0..1
+    reference: 0..1
+    status: 0..1
+    typedef: 0..n
+    unique: 0..1
+    uses: 0..n
+    when: 0..1
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children) {
+        var item, synth;
+        synth = this.require('data-synth');
+        item = (synth.Object(null)).bind(children);
+        return (synth.List(params)).set({
+          type: item
+        });
+      }
+    argument: name
+  mandatory:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, p, ctx) {
+        return ctx.mandatory = arg === true || arg === 'true';
+      }
+    argument: value
+  max-elements:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        if (arg !== 'unbounded') {
+          return ctx['max-elements'] = Number(arg);
+        }
+      }
+    argument: value
+  min-elements:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return ctx['min-elements'] = Number(arg);
+      }
+    argument: value
+  must:
+    description: 0..1
+    error-app-tag: 0..1
+    error-message: 0..1
+    reference: 0..1
+    argument: condition
+  notification:
+    anyxml: 0..n
+    choice: 0..n
+    container: 0..n
+    description: 0..1
+    grouping: 0..n
+    if-feature: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    reference: 0..1
+    status: 0..1
+    typedef: 0..n
+    uses: 0..n
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params) {
+        return this.define('notification', arg, params);
+      }
+    argument: event
+  output:
+    anyxml: 0..n
+    choice: 0..n
+    container: 0..n
+    grouping: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    typedef: 0..n
+    uses: 0..n
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children) {
+        var synth;
+        synth = this.require('data-synth');
+        return (synth.Object(params)).bind(children);
+      }
+    instance: 0..n
+    instance-list: 0..n
+    origin:
+      anyxml: 0..n
+      choice: 0..n
+      container: 0..n
+      grouping: 0..n
+      leaf: 0..n
+      leaf-list: 0..n
+      list: 0..n
+      typedef: 0..n
+      uses: 0..n
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children) {
+          var synth;
+          synth = this.require('data-synth');
+          return (synth.Object(params)).bind(children);
+        }
+  path:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return ctx.path = arg.replace(/[_]/g, '.');
+      }
+    argument: value
+  pattern:
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children, ctx) {
+        if (ctx.patterns == null) {
+          ctx.patterns = [];
+        }
+        return ctx.patterns.push(new RegExp(arg));
+      }
+    argument: value
+  refine:
+    default: 0..1
+    description: 0..1
+    reference: 0..1
+    config: 0..1
+    mandatory: 0..1
+    presence: 0..1
+    must: 0..n
+    min-elements: 0..1
+    max-elements: 0..1
+    units: 0..1
+    argument: target-node
+  require-instance:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return ctx['require-instance'] = arg === true || arg === 'true';
+      }
+    argument: value
+  revision:
+    description: 0..1
+    reference: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return this.define('revision', arg, params);
+      }
+    argument: date
+  rpc:
+    description: 0..1
+    grouping: 0..n
+    if-feature: 0..n
+    input: 0..1
+    output: 0..1
+    reference: 0..1
+    status: 0..1
+    typedef: 0..n
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children) {
+        var func, method, ref, ref1, request, response, synth;
+        synth = this.require('data-synth');
+        func = this.resolve('rpc', arg, false);
+        if (func == null) {
+          func = function(input, output, done) {
+            return done("No control logic found for '" + arg + "' rpc operation");
+          };
+        }
+        request = (ref = children.input) != null ? ref : synth.Meta;
+        response = (ref1 = children.output) != null ? ref1 : synth.Meta;
+        method = function(data, resolve, reject) {
+          var e, error, input, output;
+          if (typeof console.debug === "function") {
+            console.debug("executing rpc " + arg + "...");
+          }
+          try {
+            input = new request(data, this);
+            output = new response(null, this);
+          } catch (error) {
+            e = error;
+            return reject(e);
+          }
+          return func.call(this, input, output, function(e) {
+            if (e == null) {
+              return resolve(output);
+            } else {
+              return reject(e);
+            }
+          });
+        };
+        method.params = params;
+        method.input = request;
+        method.output = response;
+        return method;
+      }
+    argument: name
+  submodule:
+    argument: name
+    anyxml: 0..n
+    augment: 0..n
+    belongs-to: 0..1
+    choice: 0..n
+    contact: 0..1
+    container: 0..n
+    description: 0..1
+    deviation: 0..n
+    extension: 0..n
+    feature: 0..n
+    grouping: 0..n
+    identity: 0..n
+    import: 0..n
+    include: 0..n
+    leaf: 0..n
+    leaf-list: 0..n
+    list: 0..n
+    notification: 0..n
+    organization: 0..1
+    reference: 0..1
+    revision: 0..n
+    rpc: 0..n
+    typedef: 0..n
+    uses: 0..n
+    yang-version: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        var k, v;
+        for (k in params) {
+          v = params[k];
+          ctx[k] = v;
+        }
+        return delete ctx.submodule;
+      }
+    complex-type: 0..n
+    instance: 0..n
+    instance-list: 0..n
+    origin:
+      argument: name
+      anyxml: 0..n
+      augment: 0..n
+      belongs-to: 0..1
+      choice: 0..n
+      contact: 0..1
+      container: 0..n
+      description: 0..1
+      deviation: 0..n
+      extension: 0..n
+      feature: 0..n
+      grouping: 0..n
+      identity: 0..n
+      import: 0..n
+      include: 0..n
+      leaf: 0..n
+      leaf-list: 0..n
+      list: 0..n
+      notification: 0..n
+      organization: 0..1
+      reference: 0..1
+      revision: 0..n
+      rpc: 0..n
+      typedef: 0..n
+      uses: 0..n
+      yang-version: 0..1
+      preprocess: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, ctx) {
+          var k, v;
+          for (k in params) {
+            v = params[k];
+            ctx[k] = v;
+          }
+          return delete ctx.submodule;
+        }
+  status:
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return ctx.status != null ? ctx.status : ctx.status = arg;
+      }
+    argument: value
+  type:
+    base: 0..1
+    bit: 0..n
+    enum: 0..n
+    fraction-digits: 0..1
+    length: 0..1
+    path: 0..1
+    pattern: 0..n
+    range: 0..1
+    require-instance: 0..1
+    type: 0..n
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        return delete this.enumValue;
+      }
+    construct: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, children, ctx, self) {
+        if (children.ctype != null) {
+          ctx.type = children.ctype;
+          return null;
+        } else {
+          return self.origin.construct.apply(this, arguments);
+        }
+      }
+    argument: name
+    instance-type: 0..1
+    origin:
+      base: 0..1
+      bit: 0..n
+      enum: 0..n
+      fraction-digits: 0..1
+      length: 0..1
+      path: 0..1
+      pattern: 0..n
+      range: 0..1
+      require-instance: 0..1
+      type: 0..n
+      preprocess: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, ctx) {
+          return delete this.enumValue;
+        }
+      construct: !<tag:yaml.org,2002:js/function> |-
+        function (arg, params, children, ctx) {
+          var key, mparams, ref, ref1, synth, typedef, value;
+          synth = this.require('data-synth');
+          typedef = this.resolve('typedef', arg);
+          if (typedef == null) {
+            throw this.error("unable to resolve typedef for " + arg);
+          }
+          switch (false) {
+            case typedef.construct == null:
+              ctx.type = typedef.construct(params, this, arguments.callee);
+              break;
+            case typeof typedef.type !== 'object':
+              ref = typedef.type;
+              for (key in ref) {
+                value = ref[key];
+                mparams = synth.copy({}, value);
+                synth.copy(mparams, params);
+                arguments.callee.call(this, key, mparams, children, ctx);
+              }
+              break;
+            case typeof typedef.type !== 'string':
+              arguments.callee.call(this, typedef.type, params, children, ctx);
+          }
+          if ((ref1 = ctx.type) != null) {
+            ref1.toString = function() {
+              return arg;
+            };
+          }
+          return null;
+        }
+      argument: name
+  typedef:
+    default: 0..1
+    description: 0..1
+    units: 0..1
+    type: 0..1
+    reference: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params) {
+        return this.define('typedef', arg, params);
+      }
+    argument: name
+  uses:
+    augment: 0..n
+    description: 0..1
+    if-feature: 0..n
+    refine: 0..n
+    reference: 0..1
+    status: 0..1
+    when: 0..1
+    preprocess: !<tag:yaml.org,2002:js/function> |-
+      function (arg, params, ctx) {
+        var changes, grouping, k, match, ref, ref1, synth, target, v;
+        synth = this.require('data-synth');
+        grouping = synth.copy({}, this.resolve('grouping', arg));
+        delete grouping.description;
+        delete grouping.reference;
+        synth.copy(ctx, grouping);
+        ref = params.refine;
+        for (target in ref) {
+          changes = ref[target];
+          match = this.locate(ctx, target);
+          if (match == null) {
+            continue;
+          }
+          for (k in changes) {
+            v = changes[k];
+            match[k] = v;
+          }
+        }
+        ref1 = params.augment;
+        for (target in ref1) {
+          changes = ref1[target];
+          match = this.locate(ctx, target);
+          if (match == null) {
+            continue;
+          }
+          synth.copy(match, changes);
+        }
+        if (typeof ctx.uses === 'object') {
+          return delete ctx.uses[arg];
+        } else {
+          return delete ctx.uses;
+        }
+      }
+    argument: name
+  when:
+    description: 0..1
+    reference: 0..1
+    argument: condition
+  anyxml: {}
+  base:
+    argument: name
+  contact:
+    argument:
+      text:
+        yin-element: 'true'
+  default:
+    argument: value
+  description:
+    argument:
+      text:
+        yin-element: 'true'
+  error-app-tag:
+    argument: value
+  error-message:
+    argument:
+      value:
+        yin-element: 'true'
+  fraction-digits:
+    argument: value
+  key:
+    argument: value
+  length:
+    argument: value
+  namespace:
+    argument: uri
+  ordered-by:
+    argument: value
+  organization:
+    argument:
+      text:
+        yin-element: 'true'
+  position:
+    argument: value
+  presence:
+    argument: value
+  range:
+    argument: value
+  reference:
+    argument:
+      text:
+        yin-element: 'true'
+  revision-date:
+    argument: date
+  unique:
+    argument: tag
+  units:
+    argument: value
+  value:
+    argument: value
+  yang-version:
+    argument: value
+  yin-element:
+    argument: value
+feature: !<tag:yaml.org,2002:js/undefined> ''
+keywords:
+  - opnfv
+  - promise
+  - vim
+  - nfvi
+  - infrastructure
+  - openstack
+  - nbi
+  - yangforge
+  - resource
+  - reservation
+  - capacity
+  - allocation
+rpc:
+  create-reservation: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var reservation, reservations;
+          reservation = this.create('ResourceReservation');
+          reservations = this.access('promise.reservations');
+          return reservation.invoke('update', input.get()).then(function(res) {
+            return res.save().then(function() {
+              reservations.push(res);
+              output.set({
+                result: 'ok',
+                message: 'reservation request accepted'
+              });
+              output.set('reservation-id', res.id);
+              return done();
+            })["catch"](function(err) {
+              output.set({
+                result: 'error',
+                message: err
+              });
+              return done();
+            });
+          })["catch"](function(err) {
+            output.set({
+              result: 'conflict',
+              message: err
+            });
+            return done();
+          });
+        }
+  query-reservation: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var query;
+          query = input.get();
+          query.capacity = 'reserved';
+          return this.invoke('query-capacity', query).then(function(res) {
+            output.set('reservations', res.get('collections'));
+            output.set('utilization', res.get('utilization'));
+            return done();
+          })["catch"](function(e) {
+            return done(e);
+          });
+        }
+  update-reservation: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var reservation;
+          if ((input.get('reservation-id')) == null) {
+            output.set({
+              result: 'error',
+              message: "must provide 'reservation-id' parameter"
+            });
+            return done();
+          }
+          reservation = this.find('ResourceReservation', input.get('reservation-id'));
+          if (reservation == null) {
+            output.set({
+              result: 'error',
+              message: 'no reservation found for specified identifier'
+            });
+            return done();
+          }
+          return reservation.invoke('update', input.get()).then(function(res) {
+            return res.save().then(function() {
+              output.set({
+                result: 'ok',
+                message: 'reservation update successful'
+              });
+              return done();
+            })["catch"](function(err) {
+              output.set({
+                result: 'error',
+                message: err
+              });
+              return done();
+            });
+          })["catch"](function(err) {
+            output.set({
+              result: 'conflict',
+              message: err
+            });
+            return done();
+          });
+        }
+  cancel-reservation: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var reservation;
+          reservation = this.find('ResourceReservation', input.get('reservation-id'));
+          if (reservation == null) {
+            output.set({
+              result: 'error',
+              message: 'no reservation found for specified identifier'
+            });
+            return done();
+          }
+          return reservation.destroy().then((function(_this) {
+            return function() {
+              (_this.access('promise.reservations')).remove(reservation.id);
+              output.set('result', 'ok');
+              output.set('message', 'reservation canceled');
+              return done();
+            };
+          })(this))["catch"](function(e) {
+            output.set('result', 'error');
+            output.set('message', e);
+            return done();
+          });
+        }
+  query-capacity: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var collections, deltas, entry, k, last, matches, metric, timestamp, usages, v, window;
+          window = input.get('window');
+          metric = input.get('capacity');
+          collections = (function() {
+            switch (metric) {
+              case 'total':
+                return ['ResourcePool'];
+              case 'reserved':
+                return ['ResourceReservation'];
+              case 'usage':
+                return ['ResourceAllocation'];
+              case 'available':
+                return ['ResourcePool', 'ResourceReservation', 'ResourceAllocation'];
+            }
+          })();
+          matches = collections.reduce(((function(_this) {
+            return function(a, name) {
+              var res;
+              res = _this.find(name, {
+                start: function(value) {
+                  return (window.end == null) || (new Date(value)) <= (new Date(window.end));
+                },
+                end: function(value) {
+                  return (window.start == null) || (new Date(value)) >= (new Date(window.start));
+                },
+                enabled: true
+              });
+              return a.concat.apply(a, res);
+            };
+          })(this)), []);
+          if (window.scope === 'exclusive') {
+            matches = matches.where({
+              start: function(value) {
+                return (window.start == null) || (new Date(value)) >= (new Date(window.start));
+              },
+              end: function(value) {
+                return (window.end == null) || (new Date(value)) <= (new Date(window.end));
+              }
+            });
+          }
+          matches = matches.without({
+            id: input.get('without')
+          });
+          if (metric === 'available') {
+            matches = matches.without({
+              reservation: function(v) {
+                return v != null;
+              }
+            });
+          }
+          output.set('collections', matches);
+          if ((input.get('show-utilization')) !== true) {
+            return done();
+          }
+          deltas = matches.reduce((function(a, entry) {
+            var b, base, base1, ekey, k, ref1, ref2, skey, v;
+            b = entry.get();
+            if (b.end == null) {
+              b.end = 'infiniteT';
+            }
+            ref1 = [(b.start.split('T'))[0], (b.end.split('T'))[0]], skey = ref1[0], ekey = ref1[1];
+            if (a[skey] == null) {
+              a[skey] = {
+                count: 0,
+                capacity: {}
+              };
+            }
+            if (a[ekey] == null) {
+              a[ekey] = {
+                count: 0,
+                capacity: {}
+              };
+            }
+            a[skey].count += 1;
+            a[ekey].count -= 1;
+            ref2 = b.capacity;
+            for (k in ref2) {
+              v = ref2[k];
+              if (!(v != null)) {
+                continue;
+              }
+              if ((base = a[skey].capacity)[k] == null) {
+                base[k] = 0;
+              }
+              if ((base1 = a[ekey].capacity)[k] == null) {
+                base1[k] = 0;
+              }
+              if (entry.name === 'ResourcePool') {
+                a[skey].capacity[k] += v;
+                a[ekey].capacity[k] -= v;
+              } else {
+                a[skey].capacity[k] -= v;
+                a[ekey].capacity[k] += v;
+              }
+            }
+            return a;
+          }), {});
+          last = {
+            count: 0,
+            capacity: {}
+          };
+          usages = (function() {
+            var i, len, ref1, ref2, ref3, results;
+            ref1 = Object.keys(deltas).sort();
+            results = [];
+            for (i = 0, len = ref1.length; i < len; i++) {
+              timestamp = ref1[i];
+              if (!(timestamp !== 'infinite')) {
+                continue;
+              }
+              entry = deltas[timestamp];
+              entry.timestamp = (new Date(timestamp)).toJSON();
+              entry.count += last.count;
+              ref2 = entry.capacity;
+              for (k in ref2) {
+                v = ref2[k];
+                entry.capacity[k] += (ref3 = last.capacity[k]) != null ? ref3 : 0;
+              }
+              last = entry;
+              results.push(entry);
+            }
+            return results;
+          })();
+          output.set('utilization', usages);
+          return done();
+        }
+  increase-capacity: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var pool;
+          pool = this.create('ResourcePool', input.get());
+          return pool.save().then((function(_this) {
+            return function(res) {
+              (_this.access('promise.pools')).push(res);
+              output.set({
+                result: 'ok',
+                message: 'capacity increase successful'
+              });
+              output.set('pool-id', res.id);
+              return done();
+            };
+          })(this))["catch"](function(e) {
+            output.set({
+              result: 'error',
+              message: e
+            });
+            return done();
+          });
+        }
+  decrease-capacity: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var k, pool, ref1, request, v;
+          request = input.get();
+          ref1 = request.capacity;
+          for (k in ref1) {
+            v = ref1[k];
+            request.capacity[k] = -v;
+          }
+          pool = this.create('ResourcePool', request);
+          return pool.save().then((function(_this) {
+            return function(res) {
+              (_this.access('promise.pools')).push(res);
+              output.set({
+                result: 'ok',
+                message: 'capacity decrease successful'
+              });
+              output.set('pool-id', res.id);
+              return done();
+            };
+          })(this))["catch"](function(e) {
+            output.set({
+              result: 'error',
+              message: e
+            });
+            return done();
+          });
+        }
+  create-instance: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var available, flavor, k, pid, provider, required, reservation, rid, v;
+          pid = input.get('provider-id');
+          if (pid != null) {
+            provider = this.find('ResourceProvider', pid);
+            if (provider == null) {
+              output.set({
+                result: 'error',
+                message: "no matching provider found for specified identifier: " + pid
+              });
+              return done();
+            }
+          } else {
+            provider = (this.find('ResourceProvider'))[0];
+            if (provider == null) {
+              output.set({
+                result: 'error',
+                message: "no available provider found for create-instance"
+              });
+              return done();
+            }
+          }
+          flavor = provider.access("services.compute.flavors." + (input.get('flavor')));
+          if (flavor == null) {
+            output.set({
+              result: 'error',
+              message: "no such flavor found for specified identifier: " + pid
+            });
+            return done();
+          }
+          required = {
+            instances: 1,
+            cores: flavor.get('vcpus'),
+            ram: flavor.get('ram'),
+            gigabytes: flavor.get('disk')
+          };
+          rid = input.get('reservation-id');
+          if (rid != null) {
+            reservation = this.find('ResourceReservation', rid);
+            if (reservation == null) {
+              output.set({
+                result: 'error',
+                message: 'no valid reservation found for specified identifier'
+              });
+              return done();
+            }
+            if ((reservation.get('active')) !== true) {
+              output.set({
+                result: 'error',
+                message: "reservation is currently not active"
+              });
+              return done();
+            }
+            available = reservation.get('remaining');
+          } else {
+            available = this.get('promise.capacity.available');
+          }
+          for (k in required) {
+            v = required[k];
+            if ((v != null) && !!v) {
+              if (!(available[k] >= v)) {
+                output.set({
+                  result: 'conflict',
+                  message: "required " + k + "=" + v + " exceeds available " + available[k]
+                });
+                return done();
+              }
+            }
+          }
+          return this.create('ResourceAllocation', {
+            reservation: rid,
+            capacity: required
+          }).save().then((function(_this) {
+            return function(instance) {
+              var request, url;
+              url = provider.get('services.compute.endpoint');
+              request = _this.parent.require('superagent');
+              request.post(url + "/servers").send({
+                server: {
+                  name: input.get('name'),
+                  imageRef: input.get('image'),
+                  flavorRef: input.get('flavor')
+                }
+              }).set('X-Auth-Token', provider.get('token')).set('Accept', 'application/json').end(function(err, res) {
+                if ((err != null) || !res.ok) {
+                  instance.destroy();
+                  console.error(err);
+                  return done(res.error);
+                }
+                instance.set('instance-ref', {
+                  provider: provider,
+                  server: res.body.server.id
+                });
+                (_this.access('promise.allocations')).push(instance);
+                output.set({
+                  result: 'ok',
+                  message: 'create-instance request accepted'
+                });
+                output.set('instance-id', instance.id);
+                return done();
+              });
+              return instance;
+            };
+          })(this))["catch"](function(err) {
+            output.set({
+              result: 'error',
+              mesage: err
+            });
+            return done();
+          });
+        }
+  destroy-instance: !<tag:yaml.org,2002:js/function> |-
+    function (input, output, done) {
+          var instance;
+          instance = this.find('ResourceAllocation', input.get('instance-id'));
+          if (instance == null) {
+            output.set({
+              result: 'error',
+              message: 'no allocation found for specified identifier'
+            });
+            return done();
+          }
+          return instance.destroy().then((function(_this) {
+            return function() {
+              var provider, ref, request, url;
+              (_this.access('promise.allocations')).remove(instance.id);
+              ref = instance.get('instance-ref');
+              provider = _this.access("promise.providers." + ref.provider);
+              url = provider.get('services.compute.endpoint');
+              request = _this.parent.require('superagent');
+              request["delete"](url + "/servers/" + ref.server).set('X-Auth-Token', provider.get('token')).set('Accept', 'application/json').end(function(err, res) {
+                if ((err != null) || !res.ok) {
+                  console.error(err);
+                  return done(res.error);
+                }
+                output.set('result', 'ok');
+                output.set('message', 'instance destroyed and resource released back to pool');
+                return done();
+              });
+              return instance;
+            };
+          })(this))["catch"](function(e) {
+            output.set('result', 'error');
+            output.set('message', e);
+            return done();
+          });
+        }
+  add-provider: !<tag:yaml.org,2002:js/function> "function (input, output, done) {\n      var app, payload, providers, request, url;\n      app = this.parent;\n      request = app.require('superagent');\n      payload = (function() {\n        switch (input.get('provider-type')) {\n          case 'openstack':\n            return {\n              auth: {\n                tenantId: input.get('tenant.id'),\n                tenantName: input.get('tenant.name'),\n                passwordCredentials: input.get('username', 'password')\n              }\n            };\n        }\n      })();\n      if (payload == null) {\n        return done('Sorry, only openstack supported at this time');\n      }\n      url = input.get('endpoint');\n      switch (input.get('strategy')) {\n        case 'keystone':\n        case 'oauth':\n          if (!/\\/tokens$/.test(url)) {\n            url += '/tokens';\n          }\n      }\n      providers = this.access('promise.providers');\n      return request.post(url).send(payload).set('Accept', 'application/json').end((function(_this) {\n        return function(err, res) {\n          var access, provider, ref1, ref2, ref3;\n          if ((err != null) || !res.ok) {\n            return done(res.error);\n          }\n          access = res.body.access;\n          provider = _this.create('ResourceProvider', {\n            token: access != null ? (ref1 = access.token) != null ? ref1.id : void 0 : void 0,\n            name: access != null ? (ref2 = access.token) != null ? (ref3 = ref2.tenant) != null ? ref3.name : void 0 : void 0 : void 0\n          });\n          return provider.invoke('update', access.serviceCatalog).then(function(res) {\n            return res.save().then(function() {\n              providers.push(res);\n              output.set('result', 'ok');\n              output.set('provider-id', res.id);\n              return done();\n            })[\"catch\"](function(err) {\n              output.set('error', {\n                message: err\n              });\n              return done();\n            });\n          })[\"catch\"](function(err) {\n            output.set('error', {\n              message: err\n            });\n            return done();\n          });\n        };\n      })(this));\n    }"
+typedef: !<tag:yaml.org,2002:js/undefined> ''
+complex-type:
+  ResourceElement:
+    id: !<tag:yaml.org,2002:js/function> |-
+      function (prev) {
+        return prev.set('default', function() {
+          return this.uuid();
+        });
+      }
+  ResourceCollection:
+    id:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return this.uuid();
+          });
+        }
+    start:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return (new Date).toJSON();
+          });
+        }
+    active:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var end, now, start;
+            now = new Date;
+            start = new Date(this.get('start'));
+            end = (function() {
+              switch (false) {
+                case (this.get('end')) == null:
+                  return new Date(this.get('end'));
+                default:
+                  return now;
+              }
+            }).call(this);
+            return (this.get('enabled')) && ((start <= now && now <= end));
+          }), {
+            type: prev
+          });
+        }
+  ResourceReservation:
+    id:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return this.uuid();
+          });
+        }
+    start:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return (new Date).toJSON();
+          });
+        }
+    active:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var end, now, start;
+            now = new Date;
+            start = new Date(this.get('start'));
+            end = (function() {
+              switch (false) {
+                case (this.get('end')) == null:
+                  return new Date(this.get('end'));
+                default:
+                  return now;
+              }
+            }).call(this);
+            return (this.get('enabled')) && ((start <= now && now <= end));
+          }), {
+            type: prev
+          });
+        }
+    end:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            var end, max;
+            end = new Date(this.get('start'));
+            max = this.parent.get('promise.policy.reservation.max-duration');
+            if (max == null) {
+              return;
+            }
+            end.setTime(end.getTime() + (max * 60 * 60 * 1000));
+            return end.toJSON();
+          });
+        }
+    allocations:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var res;
+            res = this.store.find('ResourceAllocation', {
+              reservation: this.id
+            });
+            return res.map(function(x) {
+              return x.get('id');
+            });
+          }), {
+            type: 'array'
+          });
+        }
+    remaining:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var entry, i, k, len, records, total, usage, v;
+            total = this.get('capacity');
+            records = this.store.find('ResourceAllocation', {
+              id: this.get('allocations'),
+              active: true
+            });
+            for (i = 0, len = records.length; i < len; i++) {
+              entry = records[i];
+              usage = entry.get('capacity');
+              for (k in usage) {
+                v = usage[k];
+                total[k] -= v;
+              }
+            }
+            return total;
+          }), {
+            type: prev
+          });
+        }
+    validate:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return function(value, resolve, reject) {
+            var end, hasCapacity, k, now, ref, start, v;
+            if (value == null) {
+              value = {};
+            }
+            ref = value.capacity;
+            for (k in ref) {
+              v = ref[k];
+              if ((v != null) && !!v) {
+                hasCapacity = true;
+              }
+            }
+            if ((!hasCapacity) && value.elements.length === 0) {
+              return reject("unable to validate reservation record without anything being reserved");
+            }
+            now = new Date;
+            if (value.start != null) {
+              start = new Date(value.start);
+            }
+            if (value.end != null) {
+              end = new Date(value.end);
+            }
+            if ((end != null) && end < now) {
+              return reject("requested end time " + value.end + " cannot be in the past");
+            }
+            if ((start != null) && (end != null) && start > end) {
+              retun(reject("requested start time must be earlier than end time"));
+            }
+            return resolve(this);
+          };
+        }
+    update:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return function(req, resolve, reject) {
+            if (req.start == null) {
+              req.start = this.get('start');
+            }
+            if (req.end == null) {
+              req.end = this.get('end');
+            }
+            return this.parent.invoke('query-capacity', {
+              start: req.start,
+              end: req.end,
+              capacity: 'available',
+              without: this.id
+            }).then((function(_this) {
+              return function(res) {
+                var available, collections, end, entries, i, k, pools, ref, ref1, start, t1, t2, v, x;
+                collections = res.get('collections');
+                if (!(collections.length > 0)) {
+                  return reject('no resource capacity available during requested start/end time');
+                }
+                pools = collections.filter(function(e) {
+                  return /^ResourcePool/.test(e);
+                });
+                entries = res.get('utilization');
+                start = new Date(req.start);
+                end = new Date(req.end);
+                for (x = i = 0, ref = entries.length - 1; 0 <= ref ? i <= ref : i >= ref; x = 0 <= ref ? ++i : --i) {
+                  t1 = new Date(entries[x].timestamp);
+                  if (!(t1 < end)) {
+                    break;
+                  }
+                  if (x < entries.length - 1) {
+                    t2 = new Date(entries[x + 1].timestamp);
+                    if (!(t2 > start)) {
+                      continue;
+                    }
+                  }
+                  available = entries[x].capacity;
+                  ref1 = req.capacity;
+                  for (k in ref1) {
+                    v = ref1[k];
+                    if ((v != null) && !!v) {
+                      if (!(available[k] >= v)) {
+                        return reject("requested " + k + "=" + v + " exceeds available " + available[k] + " between " + t1 + " and " + t2);
+                      }
+                    }
+                  }
+                }
+                _this.set(req);
+                _this.set('pools', pools);
+                return resolve(_this);
+              };
+            })(this))["catch"](function(err) {
+              return reject(err);
+            });
+          };
+        }
+    save:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return function(resolve, reject) {
+            return this.invoke('validate', this.get()).then(function(res) {
+              var now;
+              now = (new Date).toJSON();
+              if ((res.get('created-on')) == null) {
+                res.set('created-on', now);
+              }
+              res.set('modified-on', now);
+              return resolve(res);
+            })["catch"](function(e) {
+              return reject(e);
+            });
+          };
+        }
+  ResourceAllocation:
+    id:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return this.uuid();
+          });
+        }
+    start:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return (new Date).toJSON();
+          });
+        }
+    active:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var end, now, start;
+            now = new Date;
+            start = new Date(this.get('start'));
+            end = (function() {
+              switch (false) {
+                case (this.get('end')) == null:
+                  return new Date(this.get('end'));
+                default:
+                  return now;
+              }
+            }).call(this);
+            return (this.get('enabled')) && ((start <= now && now <= end));
+          }), {
+            type: prev
+          });
+        }
+    priority:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            switch (false) {
+              case !((this.get('reservation')) == null):
+                return 3;
+              case !!(this.get('active')):
+                return 2;
+              default:
+                return 1;
+            }
+          }), {
+            type: prev
+          });
+        }
+  ResourcePool:
+    id:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return this.uuid();
+          });
+        }
+    start:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return (new Date).toJSON();
+          });
+        }
+    active:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var end, now, start;
+            now = new Date;
+            start = new Date(this.get('start'));
+            end = (function() {
+              switch (false) {
+                case (this.get('end')) == null:
+                  return new Date(this.get('end'));
+                default:
+                  return now;
+              }
+            }).call(this);
+            return (this.get('enabled')) && ((start <= now && now <= end));
+          }), {
+            type: prev
+          });
+        }
+    save:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return function(resolve, reject) {
+            var hasCapacity, k, ref, v, value;
+            value = this.get();
+            ref = value.capacity;
+            for (k in ref) {
+              v = ref[k];
+              if ((v != null) && !!v) {
+                hasCapacity = true;
+              }
+            }
+            if ((!hasCapacity) && value.elements.length === 0) {
+              return reject("unable to save pool record without any capacity values");
+            }
+            return resolve(this);
+          };
+        }
+  ResourceProvider:
+    id:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('default', function() {
+            return this.uuid();
+          });
+        }
+    token:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return prev.set('private', true);
+        }
+    pools:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            return (this.store.find('ResourcePool', {
+              source: this.get('name')
+            })).map(function(x) {
+              return x.get('id');
+            });
+          }), {
+            type: 'array'
+          });
+        }
+    update:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return function(services, resolve, reject) {
+            var request;
+            if (services == null) {
+              services = [];
+            }
+            if (!services.length) {
+              return reject("unable to update provider without list of services");
+            }
+            request = this.store.parent.require('superagent');
+            services.forEach((function(_this) {
+              return function(service) {
+                var url;
+                switch (service.type) {
+                  case 'compute':
+                    url = service.endpoints[0].publicURL;
+                    _this.set('services.compute.endpoint', url);
+                    request.get(url + "/limits").set('X-Auth-Token', _this.get('token')).set('Accept', 'application/json').end(function(err, res) {
+                      var capacity, ref;
+                      if ((err != null) || !res.ok) {
+                        console.warn("request to discover capacity limits failed");
+                        return;
+                      }
+                      capacity = (ref = res.body.limits) != null ? ref.absolute : void 0;
+                      return (_this.access('capacity')).set({
+                        cores: capacity.maxTotalCores,
+                        ram: capacity.maxTotalRAMSize,
+                        instances: capacity.maxTotalInstances,
+                        addresses: capacity.maxTotalFloatingIps
+                      });
+                    });
+                    return request.get(url + "/flavors/detail").set('X-Auth-Token', _this.get('token')).set('Accept', 'application/json').end(function(err, res) {
+                      var er, error, flavors, ref;
+                      if ((err != null) || !res.ok) {
+                        console.warn("request to discover compute flavors failed");
+                        return;
+                      }
+                      flavors = res.body.flavors;
+                      try {
+                        flavors = flavors.map(function(x) {
+                          return {
+                            ResourceFlavor: x
+                          };
+                        });
+                        return (ref = _this.access('services.compute.flavors')).push.apply(ref, flavors);
+                      } catch (error) {
+                        er = error;
+                        return console.warn("failed to update flavors into the provider due to validation errors");
+                      }
+                    });
+                }
+              };
+            })(this));
+            return resolve(this);
+          };
+        }
+  ResourceFlavor: !<tag:yaml.org,2002:js/undefined> ''
+pkgdir: /home/plee/hack/opnfv-promise
+module:
+  opnfv-promise:
+    promise.capacity.total:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var combine;
+            combine = function(a, b) {
+              var k, ref, v;
+              ref = b.capacity;
+              for (k in ref) {
+                v = ref[k];
+                if (!(v != null)) {
+                  continue;
+                }
+                if (a[k] == null) {
+                  a[k] = 0;
+                }
+                a[k] += v;
+              }
+              return a;
+            };
+            return (this.parent.get('pools')).filter(function(entry) {
+              return entry.active === true;
+            }).reduce(combine, {});
+          }), {
+            type: prev
+          });
+        }
+    promise.capacity.reserved:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var combine;
+            combine = function(a, b) {
+              var k, ref, v;
+              ref = b.remaining;
+              for (k in ref) {
+                v = ref[k];
+                if (!(v != null)) {
+                  continue;
+                }
+                if (a[k] == null) {
+                  a[k] = 0;
+                }
+                a[k] += v;
+              }
+              return a;
+            };
+            return (this.parent.get('reservations')).filter(function(entry) {
+              return entry.active === true;
+            }).reduce(combine, {});
+          }), {
+            type: prev
+          });
+        }
+    promise.capacity.usage:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var combine;
+            combine = function(a, b) {
+              var k, ref, v;
+              ref = b.capacity;
+              for (k in ref) {
+                v = ref[k];
+                if (!(v != null)) {
+                  continue;
+                }
+                if (a[k] == null) {
+                  a[k] = 0;
+                }
+                a[k] += v;
+              }
+              return a;
+            };
+            return (this.parent.get('allocations')).filter(function(entry) {
+              return entry.active === true;
+            }).reduce(combine, {});
+          }), {
+            type: prev
+          });
+        }
+    promise.capacity.available:
+      - !<tag:yaml.org,2002:js/function> |-
+        function (prev) {
+          return this.computed((function() {
+            var k, reserved, total, usage, v;
+            total = this.get('total');
+            reserved = this.get('reserved');
+            usage = this.get('usage');
+            for (k in total) {
+              v = total[k];
+              if (!(v != null)) {
+                continue;
+              }
+              if (reserved[k] != null) {
+                total[k] -= reserved[k];
+              }
+              if (usage[k] != null) {
+                total[k] -= usage[k];
+              }
+            }
+            return total;
+          }), {
+            type: prev
+          });
+        }
+config: !<tag:yaml.org,2002:js/undefined> ''
diff --git a/source/openstack.yaml b/source/openstack.yaml
new file mode 100644 (file)
index 0000000..6881186
--- /dev/null
@@ -0,0 +1,13 @@
+name: openstack
+description: OpenStack Management Module (WIP)
+author: Peter K. Lee <peter@intercloud.net>
+license: Apache-2.0
+yangforge: "0.11.x"
+
+schema: !yang schema/openstack.yang
+
+dependencies:
+  access-control-models: !yang schema/access-control-models.yang
+  nfv-infrastructure:    !yang schema/nfv-infrastructure.yang
+
+rpc: !require spec/openstack-intents.coffee
diff --git a/source/package.json b/source/package.json
new file mode 100644 (file)
index 0000000..7ae3977
--- /dev/null
@@ -0,0 +1,41 @@
+{
+  "name": "@opnfv/promise",
+  "version": "1.0.0",
+  "description": "Resource Management for Virtualized Infrastructure",
+  "author": "Peter K. Lee <peter@intercloud.net>",
+  "license": "Apache-2.0",
+  "private": true,
+  "homepage": "http://wiki.opnfv.org/promise",
+  "repository": "opnfv/promise",
+  "keywords": [
+    "opnfv",
+    "promise",
+    "vim",
+    "nfvi",
+    "infrastructure",
+    "openstack",
+    "nbi",
+    "yangforge",
+    "resource",
+    "reservation",
+    "capacity",
+    "allocation"
+  ],
+  "engines": {
+    "yangforge": ">=0.11.0"
+  },
+  "devDependencies": {
+    "config": "^1.19.0",
+    "js-yaml": "^3.5.2",
+    "mocha": "~2.0.1",
+    "promise": "^7.1.1",
+    "should": "~3.1.3",
+    "yangforge": "^0.11.0"
+  },
+  "main": "./lib/index.js",
+  "scripts": {
+    "prepublish": "yfc build -o index.yaml promise.yaml",
+    "test": "mocha",
+    "start": "yfc run --express 5050 promise.yaml"
+  }
+}
diff --git a/source/promise.yaml b/source/promise.yaml
new file mode 100644 (file)
index 0000000..125c7b7
--- /dev/null
@@ -0,0 +1,290 @@
+name: opnfv-promise
+description: Resource Management for Virtualized Infrastructure
+author: Peter K. Lee <peter@intercloud.net>
+license: Apache-2.0
+homepage: http://wiki.opnfv.org/promise
+repository: git://github.com/opnfv/promise.git
+yangforge: "0.11.x"
+keywords:
+  - opnfv
+  - promise
+  - vim
+  - nfvi
+  - infrastructure
+  - openstack
+  - nbi
+  - yangforge
+  - resource
+  - reservation
+  - capacity
+  - allocation
+
+schema: !yang schema/opnfv-promise.yang
+
+# below config provides default parameters
+# NOTE: uncomment locally for testing with pre-existing data
+#config: !json config/demo.json
+
+dependencies:
+  access-control-models: !yang schema/access-control-models.yang
+  nfv-infrastructure:    !yang schema/nfv-infrastructure.yang
+
+# MODULE model active bindings
+module:
+  opnfv-promise:
+    # rebind to be a computed property
+    promise.capacity.total: !coffee/function |
+      (prev) -> @computed (->
+        combine = (a, b) ->
+          for k, v of b.capacity when v?
+            a[k] ?= 0
+            a[k] += v
+          return a
+        (@parent.get 'pools')
+        .filter (entry) -> entry.active is true
+        .reduce combine, {}
+      ), type: prev
+    # rebind to be a computed property
+    promise.capacity.reserved: !coffee/function |
+      (prev) -> @computed (->
+        combine = (a, b) ->
+          for k, v of b.remaining when v?
+            a[k] ?= 0
+            a[k] += v
+          return a
+        (@parent.get 'reservations')
+        .filter (entry) -> entry.active is true
+        .reduce combine, {}
+      ), type: prev
+    # rebind to be a computed property
+    promise.capacity.usage: !coffee/function |
+      (prev) -> @computed (->
+        combine = (a, b) ->
+          for k, v of b.capacity when v?
+            a[k] ?= 0
+            a[k] += v
+          return a
+        (@parent.get 'allocations')
+        .filter (entry) -> entry.active is true
+        .reduce combine, {}
+      ), type: prev
+    # rebind to be a computed property
+    promise.capacity.available: !coffee/function |
+      (prev) -> @computed (->
+        total = @get 'total'
+        reserved = @get 'reserved'
+        usage = @get 'usage'
+        for k, v of total when v?
+          total[k] -= reserved[k] if reserved[k]?
+          total[k] -= usage[k] if usage[k]?
+        total
+      ), type: prev
+
+# RPC definitions (INTENT interfaces)
+rpc: !require spec/promise-intents.coffee
+
+# COMPLEX-TYPE model active bindings (controller logic)
+complex-type:
+  ResourceElement:
+    #properties
+    id: !coffee/function |
+      (prev) -> prev.set 'default', -> @uuid()
+
+  ResourceCollection:
+    # properties
+    start: !coffee/function |
+      (prev) -> prev.set 'default', -> (new Date).toJSON()
+
+    active: !coffee/function |
+      (prev) -> @computed (->
+        now = new Date
+        start = new Date (@get 'start')
+        end = switch
+          when (@get 'end')? then new Date (@get 'end')
+          else now
+        (@get 'enabled') and (start <= now <= end)
+      ), type: prev
+
+  ResourceReservation:
+    end: !coffee/function |
+      (prev) -> prev.set 'default', ->
+        end = (new Date @get 'start')
+        max = @parent.get 'promise.policy.reservation.max-duration'
+        return unless max?
+        end.setTime (end.getTime() + (max*60*60*1000))
+        end.toJSON()
+
+    allocations: !coffee/function |
+      (prev) -> @computed (->
+        res = (@store.find 'ResourceAllocation', reservation: @id)
+        res.map (x) -> x.get 'id'
+      ), type: 'array'
+
+    remaining: !coffee/function |
+      (prev) -> @computed (->
+        total = @get 'capacity'
+        records = @store.find 'ResourceAllocation', id: (@get 'allocations'), active: true
+        for entry in records
+          usage = entry.get 'capacity'
+          for k, v of usage
+            total[k] -= v
+        total
+      ), type: prev
+
+    # methods
+    validate: !coffee/function |
+      (prev) -> (value={}, resolve, reject) ->
+        # validate that request contains sufficient data
+        for k, v of value.capacity when v? and !!v
+          hasCapacity = true
+        if (not hasCapacity) and value.elements.length is 0
+          return reject "unable to validate reservation record without anything being reserved"
+        # time range verifications
+        now = new Date
+        start = (new Date value.start) if value.start?
+        end   = (new Date value.end) if value.end?
+        # if start? and start < now
+        #   return reject "requested start time #{value.start} cannot be in the past"
+        if end? and end < now
+          return reject "requested end time #{value.end} cannot be in the past"
+        if start? and end? and start > end
+          retun reject "requested start time must be earlier than end time"
+        resolve this
+
+    update: !coffee/function |
+      (prev) -> (req, resolve, reject) ->
+        req.start ?= @get 'start'
+        req.end   ?= @get 'end'
+
+        # TODO: should validate here...
+        @parent.invoke 'query-capacity',
+          start: req.start
+          end: req.end
+          capacity: 'available'
+          without: @id
+        .then (res) =>
+          collections = res.get 'collections'
+          unless collections.length > 0
+            return reject 'no resource capacity available during requested start/end time'
+
+          pools = collections.filter (e) -> /^ResourcePool/.test e
+          # should do some policy or check to see if more than one pool acceptable to reservee
+
+          entries = res.get 'utilization'
+          start = new Date req.start
+          end   = new Date req.end
+
+          for x in [0..entries.length-1]
+            t1 = new Date entries[x].timestamp
+            break unless t1 < end
+
+            if x < entries.length-1
+              t2 = new Date entries[x+1].timestamp
+              continue unless t2 > start
+
+            available = entries[x].capacity
+            for k, v of req.capacity when v? and !!v
+              unless available[k] >= v
+                return reject "requested #{k}=#{v} exceeds available #{available[k]} between #{t1} and #{t2}"
+
+          @set req
+          @set 'pools', pools
+          resolve this
+        .catch (err) -> reject err
+
+    save: !coffee/function |
+      (prev) -> (resolve, reject) ->
+        @invoke 'validate', @get()
+        .then (res) ->
+          # should do something about this reservation record...
+          now = (new Date).toJSON()
+          unless (res.get 'created-on')?
+            res.set 'created-on', now
+          res.set 'modified-on', now
+          resolve res
+        .catch (e) -> reject e
+
+  ResourceAllocation:
+    # properties
+    priority: !coffee/function |
+      (prev) -> @computed (->
+        switch
+          when not (@get 'reservation')? then 3
+          when not (@get 'active') then 2
+          else 1
+      ), type: prev
+
+  ResourcePool:
+    save: !coffee/function |
+      (prev) -> (resolve, reject) ->
+        # validate that record contains sufficient data
+        value = @get()
+
+        for k, v of value.capacity when v? and !!v
+          hasCapacity = true
+        if (not hasCapacity) and value.elements.length is 0
+          return reject "unable to save pool record without any capacity values"
+
+        resolve this
+
+  ResourceProvider:
+    # properties
+    token: !coffee/function |
+      (prev) -> prev.set 'private', true
+
+    pools: !coffee/function |
+      (prev) -> @computed (->
+        (@store.find 'ResourcePool', source: (@get 'name')).map (x) -> x.get 'id'
+      ), type: 'array'
+
+    # methods
+    # XXX - this method is OpenStack-specific only, will need to revise later
+    update: !coffee/function |
+      (prev) -> (services=[], resolve, reject) ->
+        return reject "unable to update provider without list of services" unless services.length
+        request = @store.parent.require 'superagent'
+        services.forEach (service) =>
+          switch service.type
+            when 'compute'
+              url = service.endpoints[0].publicURL
+              @set 'services.compute.endpoint', url
+              request
+                .get "#{url}/limits"
+                  .set 'X-Auth-Token', @get 'token'
+                  .set 'Accept', 'application/json'
+                  .end (err, res) =>
+                    if err? or !res.ok
+                      console.warn "request to discover capacity limits failed"
+                      return
+
+                    capacity = res.body.limits?.absolute
+                    #console.log "\ndiscovered capacity:"
+                    #console.log capacity
+
+                    (@access 'capacity').set {
+                      cores: capacity.maxTotalCores
+                      ram: capacity.maxTotalRAMSize
+                      instances: capacity.maxTotalInstances
+                      addresses: capacity.maxTotalFloatingIps
+                    }
+              request
+                .get "#{url}/flavors/detail"
+                  .set 'X-Auth-Token', @get 'token'
+                  .set 'Accept', 'application/json'
+                  .end (err, res) =>
+                    if err? or !res.ok
+                      console.warn "request to discover compute flavors failed"
+                      return
+
+                    flavors = res.body.flavors
+                    # console.log "\ndiscovered flavors:"
+                    # console.log flavors
+                    try
+                      flavors = flavors.map (x) -> ResourceFlavor: x
+                      (@access 'services.compute.flavors').push flavors...
+                    catch er
+                      console.warn "failed to update flavors into the provider due to validation errors"
+
+        # XXX - update should do promise.all
+        resolve this
+
diff --git a/source/schema/access-control-models.yang b/source/schema/access-control-models.yang
new file mode 100644 (file)
index 0000000..7b4684c
--- /dev/null
@@ -0,0 +1,92 @@
+module access-control-models {
+  prefix acm;
+  namespace "urn:opnfv:promise:acm";
+
+  import complex-types { prefix ct; }
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+
+  typedef password {
+    type string {
+      length 1..255;
+    }
+  }
+
+  grouping access-credentials {
+    leaf strategy {
+      type enumeration {
+        enum oauth;
+        enum keystone;
+      }
+      default oauth;
+    }
+    leaf endpoint {
+      type inet:uri;
+      description "The target endpoint for authentication";
+      mandatory true;
+    }
+    leaf username {
+      type string;
+      mandatory true;
+    }
+    leaf password {
+      type acm:password;
+      mandatory true;
+    }
+  }
+  
+  /*********************************************
+   * Identity Models
+   *********************************************/
+
+  ct:complex-type Identity {
+    ct:abstract true;
+    description "Identity represents an administrative access model entity";
+
+    key "id";
+    leaf id { type yang:uuid; mandatory true; }
+    leaf name { type string; mandatory true; }
+    leaf description { type string; }
+    leaf enabled { type boolean; default true; }
+  }
+
+  ct:complex-type User {
+    ct:extends Identity;
+
+    leaf credential {
+      //type instance-identifier { ct:instance-type IdentityCredential; }
+      type string;
+      mandatory true;
+    }
+    
+    container contact {
+      leaf fullName { type string; }
+      leaf email { type string; }
+    }
+
+    leaf-list groups { type instance-identifer { ct:instance-type Group; } }
+    leaf domain { type instance-identifier { ct:instance-type Domain; } }
+  }
+
+  ct:complex-type Group {
+    ct:extends Identity;
+    
+    leaf-list users { type instance-identifier { ct:instance-type User; } }
+    leaf domain { type instance-identifier { ct:instance-type Domain; } }
+  }
+
+  ct:complex-type Domain {
+    ct:extends Identity;
+    description
+      "Domain represent a distinct administrative domain across
+       collection of users and groups.";
+
+    ct:instance-list users { ct:instance-type User; }
+    ct:instance-list groups { ct:instance-type Group; }
+  }
+
+  rpc create-user;
+  rpc remove-user;
+  rpc create-group;
+  rpc remove-group;
+}
diff --git a/source/schema/nfv-infrastructure.yang b/source/schema/nfv-infrastructure.yang
new file mode 100644 (file)
index 0000000..ccad269
--- /dev/null
@@ -0,0 +1,322 @@
+module nfv-infrastructure {
+  namespace "urn:opnfv:promise:nfv:infrastructure";
+  prefix nfvi;
+
+  import access-control-models { prefix acm; }
+  import ietf-inet-types { prefix inet; }
+  import ietf-yang-types { prefix yang; }
+  import complex-types { prefix ct; }
+
+  description
+    "NFV Infrastructure Data Models with complex types and typed instance
+     identifiers representing the various ResourceElements available
+     in the infrastructure across compute, network, and storage.";
+
+  revision 2015-10-13 {
+    description
+      "Introduce capacity and element collection into NFVI models";
+  }
+  
+  revision 2015-08-07 {
+    description
+      "This YANG module is modeled using 'yangforge' which natively provides
+       complex types and typed instance identifiers.  This module
+       provides various collections of resource management data models
+       for instance based management";
+  }
+
+  identity manager {
+    description "used by specific modules implementing manager role for NFVI";
+  }
+  
+  grouping compute-capacity {
+    leaf cores     { type int16; default 0; }
+    leaf ram       { type int32; default 0; units 'MB'; }
+    leaf instances { type int16; default 0; }
+  }
+
+  grouping network-capacity {
+    leaf networks  { type int16; default 0; }
+    leaf ports     { type int16; default 0; }
+    leaf routers   { type int16; default 0; }
+    leaf subnets   { type int16; default 0; }
+    leaf addresses { type int32; default 0; }
+  }
+
+  grouping storage-capacity {
+    leaf gigabytes { type int32; default 0; units 'GB'; }
+    leaf snapshots { type int16; default 0; }
+    leaf volumes   { type int16; default 0; }
+  }
+
+  grouping resource-capacity {
+    uses compute-capacity;
+    uses network-capacity;
+    uses storage-capacity;
+  }
+  
+  grouping resource-collection {
+    description
+      "Information model capturing parameters for describing a collection of
+       resource capacity and resource elements";
+    
+    container capacity { uses resource-capacity; }
+    leaf-list elements {
+      type instance-identifier {
+        ct:instance-type nfvi:ResourceElement;
+        require-instance true;
+      }
+    }
+  }
+
+  grouping resource-stack {
+    description
+      "Information model describing a NFVI resource stack comprising of
+       various resource elements across compute, network, and storage";
+    
+    ct:instance-list hosts { ct:instance-type nfvi:PhysicalHost; }
+    ct:instance-list hypervisors { ct:instance-type nfvi:Hypervisor; }
+
+    container compute {
+      description "Contains compute related resources";
+
+      ct:instance-list servers { ct:instance-type nfvi:ServerInstance; }
+      ct:instance-list images { ct:instance-type nfvi:VirtualMachineImage; }
+      ct:instance-list flavors { ct:instance-type nfvi:ComputeFlavor; }
+    }
+
+    container network {
+      description "Contains networking related resources";
+        
+      ct:instance-list networks { ct:instance-type nfvi:Network; }
+      ct:instance-list subnets { ct:instance-type nfvi:SubNetwork; }
+      ct:instance-list ports { ct:instance-type nfvi:SwitchPort; }
+      //ct:instance-list routers { ct:instance-type Router; }
+    }
+  }
+  
+  /*********************************************
+   * Abstract Models (top-level)
+   *********************************************/
+
+  ct:complex-type ResourceElement {
+    ct:abstract true;
+
+    key "id";
+    leaf id { type yang:uuid; mandatory true; }
+    leaf name { type string; }
+    leaf enabled { type boolean; default true; }
+    leaf protected { type boolean; default false; }
+    leaf owner { type instance-identifier { ct:instance-type acm:Identity; } }
+    leaf visibility {
+      description "Specify visibility level available from the perspective of 'owner'";
+      type enumeration {
+        enum public;
+        enum domain;
+        enum project;
+        enum group;
+        enum user;
+      }
+      default user;
+    }
+    leaf-list tags { type string; }
+    
+    leaf-list members {
+      description "Optionally share with explicit list of members of AccessIdentity complex-type";
+      type instance-identifier {
+        ct:instance-type acm:Identity;
+      }
+    }
+  }
+
+  ct:complex-type ResourceInstance {
+    ct:extends ResourceElement;
+    ct:abstract true;
+
+    leaf status {
+      type enumeration {
+        enum active;
+        enum inactive;
+        enum pending;
+      }
+    }
+    leaf progress {
+      type uint8 { range 0..100; }
+      default 0;
+    }
+  }
+
+  ct:complex-type ResourceContainer {
+    ct:extends ResourceInstance;
+    ct:abstract true;
+
+    description
+      "An abstract resource instance which contains a collection of capacity
+       and elements.";
+
+    uses resource-collection;
+  }
+
+  /*********************************************
+   * Compute Models
+   *********************************************/
+
+  ct:complex-type AvailabilityZone {
+    ct:extends ResourceElement;
+  }
+  
+  ct:complex-type PhysicalHost {
+    ct:extends ResourceElement;
+
+    leaf type { type string; }
+    leaf version { type string; }
+
+    leaf cpu { type uint8; }
+    leaf workload { type uint8; default 0; }
+    leaf uptime { type string; }
+
+    container ram {
+      leaf total { type uint32; units 'MB'; }
+      leaf used { type uint32; units 'MB'; }
+      leaf free { type uint32; units 'MB'; }
+    }
+    container disk {
+      leaf total { type uint32; units 'GB'; }
+      leaf used { type uint32; units 'GB'; }
+      leaf free { type uint32; units 'GB'; }
+    }
+
+    leaf-list hypervisors { type instance-identifier { ct:instance-type Hypervisor; } }
+  }
+  
+  ct:complex-type Hypervisor {
+    ct:extends PhysicalHost;
+
+    leaf host {
+      type instance-identifier { ct:instance-type PhysicalHost; }
+      mandatory true;
+    }
+    container vcpu {
+      leaf total { type uint16; }
+      leaf used { type uint16; }
+      leaf free { type uint16; }
+    }
+    leaf-list servers { type instance-identifier { ct:instance-type ServerInstance; } }
+  }
+
+  ct:complex-type ComputeElement {
+    ct:extends ResourceElement;
+    ct:abstract true;
+
+    container constraint {
+      leaf disk { type uint32; units 'GB'; default 0; }
+      leaf ram { type uint32; units 'MB'; default 0; }
+      leaf vcpu { type uint16; default 0; }
+    }
+
+    leaf-list instances {
+      description "State info about instances currently using this resource element";
+      type instance-identifier {
+        ct:instance-type ResourceInstance;
+      }
+      config false;
+    }
+  }
+
+  ct:complex-type VirtualMachineImage {
+    ct:extends ComputeElement;
+
+    container data {
+      leaf checksum { type string; mandatory true; }
+      leaf size { type uint32; units 'Bytes'; mandatory true; }
+      
+      container format {
+        leaf container {
+          type enumeration { enum ami; enum ari; enum aki; enum bare; enum ovf; }
+          default bare;
+        }
+        leaf disk {
+          type enumeration { enum ami; enum ari; enum aki; enum vhd; enum vmdk; enum raw; enum qcow2; enum vdi; enum iso; }
+        }
+      }
+      leaf content {
+        description "should be a 'private' property so only direct access retrieves content";
+        type binary;
+      }
+    }
+  }
+
+  ct:complex-type ComputeFlavor {
+    ct:extends ResourceElement;
+
+    leaf disk  { type uint32; units 'GB'; default 0; }
+    leaf ram   { type uint32; units 'MB'; default 0; }
+    leaf vcpus { type uint16; default 0; }
+  }
+
+  ct:complex-type ServerInstance {
+    ct:extends ResourceInstance;
+
+    leaf flavor {
+      type instance-identifier { ct:instance-type ComputeFlavor; }
+      mandatory true;
+    }
+    leaf image {
+      type instance-identifier { ct:instance-type VirtualMachineImage; }
+      mandatory true;
+    }
+    
+    //ct:instance metadata { ct:instance-type MetaData; }
+
+    leaf host {
+      type instance-identifier { ct:instance-type PhysicalHost; }
+    }
+
+    leaf-list connections {
+      description
+        "References to collection of NetworkingElement class objects such as
+         Network, Subnet, Port, Router that this ServerInstance is
+         connected with.";
+      type instance-identifier { ct:instance-type NetworkElement; }
+    }
+  }
+
+  /*********************************************
+   * Network Models (Work-in-Progress)
+   *********************************************/
+
+  ct:complex-type NetworkElement {
+    ct:extends ResourceElement;
+    ct:abstract true;
+  }
+
+  ct:complex-type Network {
+    ct:extends NetworkElement;
+
+    leaf-list subnets {
+      type instance-identifier { ct:instance-type SubNetwork; }
+    }
+  }
+
+  ct:complex-type SubNetwork {
+    ct:extends NetworkElement;
+
+    leaf network { type instance-identifier { ct:instance-type Network; } }
+
+    leaf-list nameservers { type string; }
+    
+    container dhcp {
+      leaf enabled { type boolean; }
+      list pools {
+        leaf start { type inet:ip-address; }
+        leaf end   { type inet:ip-address; }
+      }
+    }
+  }
+
+  ct:complex-type SwitchPort {
+    ct:extends NetworkElement;
+
+    leaf subnet { type instance-identifier { ct:instance-type SubNetwork; } }
+  }
+}
diff --git a/source/schema/nfv-mano.yang b/source/schema/nfv-mano.yang
new file mode 100644 (file)
index 0000000..0e3bbbe
--- /dev/null
@@ -0,0 +1,149 @@
+module nfv-mano {
+  namespace "urn:opnfv:promise:nfv:mano";
+  prefix mano;
+
+  import access-control-models { prefix acm; }
+  import nfv-infrastructure { prefix nfvi; }
+  import complex-types { prefix ct; }
+  import ietf-inet-types { prefix inet; }
+  import iana-crypt-hash { prefix ianach; }
+
+  description
+    "NFV Management and Orchestration Data Models with complex types and typed
+     instance identifiers representing the NFVO, VNFM, and the VIM.";
+  
+  revision 2015-09-03 {
+    description
+      "This YANG module is modeled using 'yangforge' which natively provides
+       complex types and typed instance identifiers.  This module
+       provides various collections of infrastructure management data
+       models for instance based management";
+  }
+
+  grouping provider-credentials {
+    leaf endpoint {
+      type inet:uri;
+      description "The target URL endpoint for the resource provider";
+      mandatory true;
+    }
+    leaf username {
+      type string;
+      mandatory true;
+    }
+    leaf password {
+      type acm:password;
+      mandatory true;
+    }
+    leaf region {
+      type string;
+      description "Optional specified region for the provider";
+    }
+  }
+
+  ct:complex-type ServiceOrchestrator {
+    ct:extends acm:Domain;
+    ct:abstract true;
+    // TBD
+  }
+
+  ct:complex-type ResourceOrchestrator {
+    ct:extends acm:Domain;
+    ct:abstract true;
+    // TBD
+  }
+
+  ct:complex-type VirtualNetworkFunctionManager {
+    ct:extends acm:Domain;
+    ct:abstract true;
+    // TBD
+  }
+  
+  ct:complex-type VirtualInfrastructureManager {
+    ct:extends acm:Domain;
+    ct:abstract true;
+    
+    leaf name { type string; mandatory true; }
+
+    container auth {
+      description 'Conceptual container that will be extended by explicit provider';
+      // ct:instance-list credentials { ct:instance-type AccessCredential; }
+      // ct:instance-list roles { ct:instance-type AccessRole; }
+      // ct:instance-list policies { ct:instance-type AccessPolicy; }
+    }
+
+    ct:instance-list hosts { ct:instance-type nfvi:PhysicalHost; }
+    ct:instance-list hypervisors { ct:instance-type nfvi:Hypervisor; }
+
+    container compute {
+      if-feature has-compute;
+      description "Contains compute related resources";
+
+      ct:instance-list servers { ct:instance-type nfvi:ServerInstance; }
+      ct:instance-list images { ct:instance-type nfvi:VirtualMachineImage; }
+      ct:instance-list flavors { ct:instance-type nfvi:ComputeFlavor; }
+    }
+
+    container network {
+      if-feature has-networking;
+      description "Contains networking related resources";
+        
+      ct:instance-list networks { ct:instance-type nfvi:Network; }
+      ct:instance-list subnets { ct:instance-type nfvi:SubNetwork; }
+      ct:instance-list ports { ct:instance-type nfvi:SwitchPort; }
+      //ct:instance-list routers { ct:instance-type Router; }
+    }
+  }
+
+  container stack {
+    container nfvo {
+      ct:instance service  { ct:instance-type ServiceOrchestrator; }
+      ct:instance resource { ct:instance-type ResourceOrchestrator; }
+    }
+    container vnfm {
+      ct:instance-list managers { ct:instance-type VirtualNetworkFunctionManager; }
+    }
+    container vim {
+      ct:instance-list managers { ct:instance-type VirtualInfrastructureManager; }
+    }
+  }
+
+  rpc add-vim {
+    description "This operation allows you to add a new VirtualInfrastructureManager into the NFVI stack";
+    input {
+      uses provider-credentials;
+      
+      leaf provider {
+        description "Select a specific resource provider";
+        mandatory true;
+        type enumeration {
+          enum openstack;
+          enum hp;
+          enum rackspace;
+          enum amazon {
+            status planned;
+          }
+          enum joyent {
+            status planned;
+          }
+          enum azure {
+            status planned;
+          }
+        }
+      }
+    }
+    output {
+      leaf id {
+        description "Unique identifier for the newly added provider found in /promise/providers";
+        type instance-identifier {
+          ct:instance-type VirtualInfrastructureManager;
+        }
+      }
+      leaf result {
+        type enumeration {
+          enum success;
+          enum error;
+        }
+      }
+    }
+  }
+}
diff --git a/source/schema/openstack-compute.yang b/source/schema/openstack-compute.yang
new file mode 100644 (file)
index 0000000..c3e790c
--- /dev/null
@@ -0,0 +1,72 @@
+module openstack-compute {
+  prefix os-com;
+
+  import nfv-infrastructure { prefix nfvi; }
+  import complex-types { prefix ct; }
+
+  identity nova { base nvfi:compute; }
+  
+  feature availability-zone {
+    description "Specifies whether availability zone functionality is available.";
+  }
+  feature extended-status {
+    description "Specifies whether extended status functionality is available.";
+  }
+  feature security-groups {
+    description "Specifies whether security groups functionality is available.";
+  }
+
+  ct:complex-type ServerInstance {
+    ct:extends nfvi:ServerInstance;
+
+    leaf zone {
+      if-feature availability-zone;
+      type string;
+    }
+
+    leaf project {
+      type instance-identifier { ct:instance-type nfvi:ResourceProject; }
+      mandatory true;
+    }
+
+    container extended-status {
+      if-feature extended-status;
+      leaf locked-by;
+      leaf power;
+      leaf task;
+      leaf vm;
+    }
+
+    leaf-list security-groups {
+      if-feature security-groups;
+      type instance-identifier { ct:instance-type SecurityGroup; }
+    }
+
+  }
+
+  choice version {
+    case v2.1 {
+      ct:instance-list servers { ct:instance-type ServerInstance; }
+    }
+  }
+
+  // OpenStack Nova specific RPC calls
+  rpc resize {
+    input {
+      leaf server { type instance-type { ct:instance-type ServerInstance; } }
+      // other params for resize
+    }
+  }
+  rpc backup;
+  rpc migrate;
+  rpc restore;
+  rpc evacuate;
+  rpc lock;
+  rpc unlock;
+  rpc suspend;
+  rpc resume;
+  rpc pause;
+  rpc unpause;
+  rpc inject-network;
+  rpc reset-network;
+}
diff --git a/source/schema/openstack-identity.yang b/source/schema/openstack-identity.yang
new file mode 100644 (file)
index 0000000..4b92957
--- /dev/null
@@ -0,0 +1,84 @@
+module openstack-identity {
+  namespace "urn:opnfv:promise:openstack:identity";
+  prefix os-id;
+  
+  import access-control-models { prefix acm; }
+  import nfv-infrastructure { prefix nfvi; }
+  import complex-types { prefix ct; }
+  import ietf-yang-types { prefix yang; }
+
+  description
+    "OpenStack Identity Data Models with complex types and typed instance
+     identifiers represent the various Access Control Models available
+     within OpenStack.";
+  
+  revision 2015-09-03 {
+    description
+      "This YANG module is modeled using 'yangforge' which natively provides
+       complex types and typed instance identifiers.  This module
+       provides various collections of resource management data models
+       for instance based management";
+  }
+
+  /*********************************************
+   * OpenStack Identity Models
+   *********************************************/
+
+  ct:complex-type Project {
+    ct:extends acm:Group;
+    description
+      "OpenStack Project represent a distinct resource consumption space across
+       collection of users and groups that can reserve and allocate
+       resources.";
+    
+    leaf-list groups { type instance-identifer { ct:instance-type acm:Group; } }
+
+    container resource {
+      leaf-list images {
+        if-feature vm-images;
+        type instance-identifier { ct:instance-type nfvi:VirtualMachineImage; }
+      }
+
+      leaf-list flavors {
+        if-feature compute-flavors;
+        type instance-identifier { ct:instance-type nfvi:VirtualMachineFlavor; }
+      }
+    }
+  }
+
+  ct:complex-type User {
+    ct:extends acm:User;
+    
+    description
+      "OpenStack User can also belong to multiple projects.";
+    
+    leaf-list projects { type instance-identifier { ct:instance-type Project; } }
+  }
+
+  ct:complex-type Group {
+    ct:extends acm:Group;
+
+    description
+      "OpenStack Group can also belong to multiple projects.";
+
+    leaf-list projects { type instance-identifier { ct:instance-type Project; } }
+  }
+
+  ct:complex-type Domain {
+    ct:extends acm:Domain;
+    
+    description
+      "OpenStack Domain represent a distinct administrative domain including projects.";
+    
+    ct:instance-list projects { ct:instance-type Project; }
+  }
+
+  ct:complex-type Token {
+    leaf key { type yang:uuid; }
+    leaf identity { type instance-identifier { ct:instance-type Identity; } }
+  }
+
+
+  rpc create-project;
+  rpc remove-project;
+}
diff --git a/source/schema/openstack.yang b/source/schema/openstack.yang
new file mode 100644 (file)
index 0000000..6878f7e
--- /dev/null
@@ -0,0 +1,74 @@
+module openstack {
+  prefix os;
+
+  import nfv-infrastructure { prefix nfvi; }
+  import access-control-models { prefix acm; }
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import complex-types { prefix ct; }
+
+  description
+    "OpenStack controller module";
+
+  revision 2016-01-19 {
+    description "Basic coverage of limited intents needed for Promise";
+  }
+  
+  identity openstack { base nfvi:manager; }
+  identity release { base openstack; }
+  identity distro { base openstack; }
+
+  feature os-system-admin {
+    description "OpenStack system administration capability";
+  }
+  
+  grouping os-credentials {
+    uses acm:access-credentials {
+      refine strategy {
+        default keystone;
+      }
+      refine endpoint {
+        default "http://localhost:5000/v2.0";
+      }
+    }
+    container tenant {
+      leaf id { type string; }
+      leaf name { type string; }
+    }
+  }
+  
+  // OpenStack infrastructure platform (PLACEHOLDER)
+  container platform {
+    uses nfvi:resource-stack;
+    
+    leaf release { type identityref { base release; } }
+    leaf distro  { type identityref { base distro; } }
+    
+    //ct:instance-list services { ct:instance-type OpenStackService; }
+    //ct:instance-list endpoints { ct:instance-type ServiceEndpoint; }
+  }
+
+  // OpenStack system administrator configuration tree
+  container admin {
+    if-feature os-system-admin;
+    container auth {
+      uses os-credentials;
+      leaf token { type yang:uuid; }
+    }
+  }
+
+  rpc authenticate {
+    if-feature os-system-admin;
+    input {
+      uses os-credentials;
+    }
+    output {
+      leaf token { type yang:uuid; }
+    }
+  }
+
+  rpc create-tenant {
+    if-feature os-system-admin;
+    
+  }
+}
diff --git a/source/schema/opnfv-promise.yang b/source/schema/opnfv-promise.yang
new file mode 100644 (file)
index 0000000..b606382
--- /dev/null
@@ -0,0 +1,640 @@
+module opnfv-promise {
+  namespace "urn:opnfv:promise";
+  prefix promise;
+
+  import complex-types { prefix ct; }
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import access-control-models { prefix acm; }
+  import nfv-infrastructure { prefix nfvi; }
+
+  description
+    "OPNFV Promise Resource Reservation/Allocation controller module";
+
+  revision 2015-10-05 {
+    description "Complete coverage of reservation related intents";
+  }
+  
+  revision 2015-08-06 {
+    description "Updated to incorporate YangForge framework";
+  }
+
+  revision 2015-04-16 {
+    description "Initial revision.";
+  }
+
+  feature reservation-service {
+    description "When enabled, provides resource reservation service";
+  }
+
+  feature multi-provider {
+    description "When enabled, provides resource management across multiple providers";
+  }
+
+  typedef reference-identifier {
+    description "defines valid formats for external reference id";
+    type union {
+      type yang:uuid;
+      type inet:uri;
+      type uint32;
+    }
+  }
+  
+  grouping resource-utilization {
+    container capacity {
+      container total     { description 'Conceptual container that should be extended'; }
+      container reserved  { description 'Conceptual container that should be extended'; config false; }
+      container usage     { description 'Conceptual container that should be extended'; config false; }
+      container available { description 'Conceptual container that should be extended'; config false; }
+    }
+  }
+
+  grouping temporal-resource-collection {
+    description
+      "Information model capturing resource-collection with start/end time window";
+    
+    leaf start { type yang:date-and-time; }
+    leaf end   { type yang:date-and-time; }
+    
+    uses nfvi:resource-collection;
+  }
+  
+  grouping resource-usage-request {
+    description
+      "Information model capturing available parameters to make a resource
+       usage request.";
+    reference "OPNFV-PROMISE, Section 3.4.1";
+
+    uses temporal-resource-collection {
+      refine elements {
+        description
+          "Reference to a list of 'pre-existing' resource elements that are
+         required for fulfillment of the resource-usage-request.
+
+         It can contain any instance derived from ResourceElement,
+         such as ServerInstances or even other
+         ResourceReservations. If the resource-usage-request is
+         accepted, the ResourceElement(s) listed here will be placed
+         into 'protected' mode as to prevent accidental removal.
+
+         If any of these resource elements become 'unavailable' due to
+         environmental or administrative activity, a notification will
+         be issued informing of the issue.";
+      }
+    }
+    
+    leaf zone {
+      description "Optional identifier to an Availability Zone";
+      type instance-identifier { ct:instance-type nfvi:AvailabilityZone; }
+    }
+  }
+
+  grouping query-start-end-window {
+    container window {
+      description "Matches entries that are within the specified start/end time window";
+      leaf start { type yang:date-and-time; }
+      leaf end   { type yang:date-and-time; }
+      leaf scope {
+        type enumeration {
+          enum "exclusive" {
+            description "Matches entries that start AND end within the window";
+          }
+          enum "inclusive" {
+            description "Matches entries that start OR end within the window";
+          }
+        }
+        default "inclusive";
+      }
+    }
+  }
+
+  grouping query-resource-collection {
+    uses query-start-end-window {
+      description "Match for ResourceCollection(s) that are within the specified start/end time window";
+    }
+    leaf-list without {
+      description "Excludes specified collection identifiers from the result";
+      type instance-identifier { ct:instance-type ResourceCollection; }
+    }
+    leaf show-utilization { type boolean; default true; }
+    container elements {
+      leaf-list some {
+        description "Query for ResourceCollection(s) that contain some or more of these element(s)";
+        type instance-identifier { ct:instance-type nfvi:ResourceElement; }
+      }
+      leaf-list every {
+        description "Query for ResourceCollection(s) that contain all of these element(s)";
+        type instance-identifier { ct:instance-type nfvi:ResourceElement; }
+      }
+    }
+  }
+
+  grouping common-intent-output {
+    leaf result {
+      type enumeration {
+        enum "ok";
+        enum "conflict";
+        enum "error";
+      }
+    }
+    leaf message { type string; }
+  }
+  
+  grouping utilization-output {
+    list utilization {
+      key 'timestamp';
+      leaf timestamp { type yang:date-and-time; }
+      leaf count { type int16; }
+      container capacity { uses nfvi:resource-capacity; }
+    }
+  }
+
+  ct:complex-type ResourceCollection {
+    ct:extends nfvi:ResourceContainer;
+    ct:abstract true;
+
+    description
+      "Describes an abstract ResourceCollection data model, which represents
+       a grouping of capacity and elements available during a given
+       window in time which must be extended by other resource
+       collection related models";
+
+    leaf start { type yang:date-and-time; }
+    leaf end   { type yang:date-and-time; }
+    
+    leaf active {
+      config false;
+      description
+        "Provides current state of this record whether it is enabled and within
+         specified start/end time";
+      type boolean;
+    }
+  }
+
+  ct:complex-type ResourcePool {
+    ct:extends ResourceCollection;
+
+    description
+      "Describes an instance of an active ResourcePool record, which
+       represents total available capacity and elements from a given
+       source.";
+
+    leaf source {
+      type instance-identifier {
+        ct:instance-type nfvi:ResourceContainer;
+        require-instance true;
+      }
+      mandatory true;
+    }
+
+    refine elements {
+      // following 'must' statement applies to each element
+      // NOTE: just a non-working example for now...
+      must "boolean(/source/elements/*[@id=id])" {
+        error-message "One or more of the ResourceElement(s) does not exist in the provider to be reserved";
+      }
+    }
+  }
+
+  ct:complex-type ResourceReservation {
+    ct:extends ResourceCollection;
+    
+    description
+      "Describes an instance of an accepted resource reservation request,
+       created usually as a result of 'create-reservation' request.
+
+       A ResourceReservation is a derived instance of a generic
+       ResourceCollection which has additional parameters to map the
+       pool(s) that were referenced to accept this reservation as well
+       as to track allocations made referencing this reservation.
+
+       Contains the capacities of various resource attributes being
+       reserved along with any resource elements that are needed to be
+       available at the time of allocation(s).";
+    
+    reference "OPNFV-PROMISE, Section 3.4.1";
+
+    leaf created-on  { type yang:date-and-time; config false; }
+    leaf modified-on { type yang:date-and-time; config false; }
+
+    leaf-list pools {
+      config false;
+      description
+        "Provides list of one or more pools that were referenced for providing
+         the requested resources for this reservation.  This is an
+         important parameter for informing how/where allocation
+         requests can be issued using this reservation since it is
+         likely that the total reserved resource capacity/elements are
+         made availble from multiple sources.";
+      type instance-identifier {
+        ct:instance-type ResourcePool;
+        require-instance true;
+      }
+    }
+    
+    container remaining {
+      config false;
+      description
+        "Provides visibility into total remaining capacity for this
+         reservation based on allocations that took effect utilizing
+         this reservation ID as a reference.";
+
+      uses nfvi:resource-capacity;
+    }
+    
+    leaf-list allocations {
+      config false;
+      description
+        "Reference to a collection of consumed allocations referencing
+         this reservation.";
+      type instance-identifier {
+        ct:instance-type ResourceAllocation;
+        require-instance true;
+      }
+    }
+  }
+
+  ct:complex-type ResourceAllocation {
+    ct:extends ResourceCollection;
+
+    description
+      "A ResourceAllocation record denotes consumption of resources from a
+       referenced ResourcePool.
+
+       It does not reflect an accepted request but is created to
+       represent the actual state about the ResourcePool. It is
+       created once the allocation(s) have successfully taken effect
+       on the 'source' of the ResourcePool.
+       
+       The 'priority' state indicates the classification for dealing
+       with resource starvation scenarios. Lower priority allocations
+       will be forcefully terminated to allow for higher priority
+       allocations to be fulfilled.
+
+       Allocations without reference to an existing reservation will
+       receive the lowest priority.";
+    
+    reference "OPNFV-PROMISE, Section 3.4.3";
+
+    leaf reservation {
+      description "Reference to an existing reservation identifier (optional)";
+      
+      type instance-identifier {
+        ct:instance-type ResourceReservation;
+        require-instance true;
+      }
+    }
+
+    leaf pool {
+      description "Reference to an existing resource pool from which allocation is drawn";
+      
+      type instance-identifier {
+        ct:instance-type ResourcePool;
+        require-instance true;
+      }
+    }
+
+    container instance-ref {
+      config false;
+      description
+        "Reference to actual instance identifier of the provider/server for this allocation";
+      leaf provider {
+        type instance-identifier { ct:instance-type ResourceProvider; }
+      }
+      leaf server { type yang:uuid; }
+    }
+
+    leaf priority {
+      config false;
+      description
+        "Reflects current priority level of the allocation according to classification rules";
+      type enumeration {
+        enum "high"   { value 1; }
+        enum "normal" { value 2; }
+        enum "low"    { value 3; }
+      }
+      default "normal";
+    }
+  }
+
+  ct:complex-type ResourceFlavor {
+    description "currently NOT an extension of ResourceElement.";
+    key "id";
+    leaf id    { type string; }
+    leaf name  { type string; }
+    leaf disk  { type uint32; units 'GB'; default 0; }
+    leaf ram   { type uint32; units 'MB'; default 0; }
+    leaf vcpus { type uint16; default 0; }
+  }
+  
+  ct:complex-type ResourceProvider {
+    ct:extends nfvi:ResourceContainer;
+
+    key "name";
+    leaf token { type string; mandatory true; }
+    
+    container services { // read-only
+      config false;
+      container compute {
+        leaf endpoint { type inet:uri; }
+        ct:instance-list flavors { ct:instance-type ResourceFlavor; }
+      }
+    }
+    
+    leaf-list pools {
+      config false;
+      description
+        "Provides list of one or more pools that are referencing this provider.";
+      
+      type instance-identifier {
+        ct:instance-type ResourcePool;
+        require-instance true;
+      }
+    }
+  }
+  
+  // MAIN CONTAINER
+  container promise {
+    
+    uses resource-utilization {
+      description "Describes current state info about capacity utilization info";
+      
+      augment "capacity/total"     { uses nfvi:resource-capacity; }
+      augment "capacity/reserved"  { uses nfvi:resource-capacity; }
+      augment "capacity/usage"     { uses nfvi:resource-capacity; }
+      augment "capacity/available" { uses nfvi:resource-capacity; }
+    }
+
+    ct:instance-list providers {
+      if-feature multi-provider;
+      description "Aggregate collection of all registered ResourceProvider instances for Promise resource management service";
+      ct:instance-type ResourceProvider;
+    }
+
+    ct:instance-list pools {
+      if-feature reservation-service;
+      description "Aggregate collection of all ResourcePool instances";
+      ct:instance-type ResourcePool;
+    }
+    
+    ct:instance-list reservations {
+      if-feature reservation-service;
+      description "Aggregate collection of all ResourceReservation instances";
+      ct:instance-type ResourceReservation;
+    }
+
+    ct:instance-list allocations {
+      description "Aggregate collection of all ResourceAllocation instances";
+      ct:instance-type ResourceAllocation;
+    }
+
+    container policy {
+      container reservation {
+        leaf max-future-start-range {
+          description
+            "Enforce reservation request 'start' time is within allowed range from now";
+          type uint16 { range 0..365; }
+          units "days";
+        }
+        leaf max-future-end-range {
+          description
+            "Enforce reservation request 'end' time is within allowed range from now";
+          type uint16 { range 0..365; }
+          units "days";
+        }
+        leaf max-duration {
+          description
+            "Enforce reservation duration (end-start) does not exceed specified threshold";
+          type uint16;
+          units "hours";
+          default 8760; // for now cap it at max one year as default
+        }
+        leaf expiry {
+          description
+            "Duration in minutes from start when unallocated reserved resources
+             will be released back into the pool";
+          type uint32;
+          units "minutes";
+        }
+      }
+    }
+  }
+
+  //-------------------
+  // INTENT INTERFACE
+  //-------------------
+
+  // RESERVATION INTENTS
+  rpc create-reservation {
+    if-feature reservation-service;
+    description "Make a request to the reservation system to reserve resources";
+    input {
+      uses resource-usage-request;
+    }
+    output {
+      uses common-intent-output;
+      leaf reservation-id {
+        type instance-identifier { ct:instance-type ResourceReservation; }
+      }
+    }
+  }
+  
+  rpc update-reservation {
+    description "Update reservation details for an existing reservation";
+    input {
+      leaf reservation-id {
+        type instance-identifier {
+          ct:instance-type ResourceReservation;
+          require-instance true;
+        }
+        mandatory true;
+      }
+      uses resource-usage-request;
+    }
+    output {
+      uses common-intent-output;
+    }
+  }
+  
+  rpc cancel-reservation {
+    description "Cancel the reservation and be a good steward";
+    input {
+      leaf reservation-id {
+        type instance-identifier { ct:instance-type ResourceReservation; }
+        mandatory true;
+      }
+    }
+    output {
+      uses common-intent-output;
+    }
+  }
+
+  rpc query-reservation {
+    if-feature reservation-service;
+    description "Query the reservation system to return matching reservation(s)";
+    input {
+      leaf zone { type instance-identifier { ct:instance-type nfvi:AvailabilityZone; } }
+      uses query-resource-collection;
+    }
+    output {
+      leaf-list reservations { type instance-identifier { ct:instance-type ResourceReservation; } }
+      uses utilization-output;
+    }
+  }
+  
+  // CAPACITY INTENTS
+  rpc increase-capacity {
+    description "Increase total capacity for the reservation system between a window in time";
+    input {
+      uses temporal-resource-collection;
+      leaf source {
+        type instance-identifier {
+          ct:instance-type nfvi:ResourceContainer;
+        }
+      }
+    }
+    output {
+      uses common-intent-output;
+      leaf pool-id {
+        type instance-identifier { ct:instance-type ResourcePool; }
+      }
+    }
+  }
+
+  rpc decrease-capacity {
+    description "Decrease total capacity for the reservation system between a window in time";
+    input {
+      uses temporal-resource-collection;
+      leaf source {
+        type instance-identifier {
+          ct:instance-type nfvi:ResourceContainer;
+        }
+      }
+    }
+    output {
+      uses common-intent-output;
+      leaf pool-id {
+        type instance-identifier { ct:instance-type ResourcePool; }
+      }
+    }
+  }
+
+  rpc query-capacity {
+    description "Check available capacity information about a specified resource collection";
+    input {
+      leaf capacity {
+        type enumeration {
+          enum 'total';
+          enum 'reserved';
+          enum 'usage';
+          enum 'available';
+        }
+        default 'available';
+      }
+      leaf zone { type instance-identifier { ct:instance-type nfvi:AvailabilityZone; } }
+      uses query-resource-collection;
+      // TBD: additional parameters for query-capacity
+    }
+    output {
+      leaf-list collections { type instance-identifier { ct:instance-type ResourceCollection; } }
+      uses utilization-output;
+    }
+  }
+
+  // ALLOCATION INTENTS (should go into VIM module in the future)
+  rpc create-instance {
+    description "Create an instance of specified resource(s) utilizing capacity from the pool";
+    input {
+      leaf provider-id {
+        if-feature multi-provider;
+        type instance-identifier { ct:instance-type ResourceProvider; require-instance true; }
+      }
+      leaf name   { type string; mandatory true; }
+      leaf image  {
+        type reference-identifier;
+        mandatory true;
+      }
+      leaf flavor {
+        type reference-identifier;
+        mandatory true;
+      }
+      leaf-list networks {
+        type reference-identifier;
+        description "optional, will assign default network if not provided";
+      }
+      
+      // TODO: consider supporting a template-id (such as HEAT) for more complex instantiation
+      
+      leaf reservation-id {
+        type instance-identifier { ct:instance-type ResourceReservation; require-instance true; }
+      }
+    }
+    output {
+      uses common-intent-output;
+      leaf instance-id {
+        type instance-identifier { ct:instance-type ResourceAllocation; }
+      }
+    }
+  }
+
+  rpc destroy-instance {
+    description "Destroy an instance of resource utilization and release it back to the pool";
+    input {
+      leaf instance-id {
+        type instance-identifier { ct:instance-type ResourceAllocation; require-instance true; }
+      }
+    }
+    output {
+      uses common-intent-output;
+    }
+  }
+
+  // PROVIDER INTENTS (should go into VIM module in the future)
+  rpc add-provider {
+    description "Register a new resource provider into reservation system";
+    input {
+      leaf provider-type {
+        description "Select a specific resource provider type";
+        mandatory true;
+        type enumeration {
+          enum openstack;
+          enum hp;
+          enum rackspace;
+          enum amazon {
+            status planned;
+          }
+          enum joyent {
+            status planned;
+          }
+          enum azure {
+            status planned;
+          }
+        }
+        default openstack;
+      }
+      uses acm:access-credentials {
+        refine strategy {
+          default keystone;
+        }
+        refine endpoint {
+          default "http://localhost:5000/v2.0";
+        }
+      }
+      container tenant {
+        leaf id { type string; }
+        leaf name { type string; }
+      }
+    }
+    output {
+      uses common-intent-output;
+      leaf provider-id {
+        type instance-identifier { ct:instance-type ResourceProvider; }
+      }
+    }
+  }
+  
+  // TODO...
+  notification reservation-event;
+  notification capacity-event;
+  notification allocation-event;
+}
diff --git a/source/spec/openstack-intents.coffee b/source/spec/openstack-intents.coffee
new file mode 100644 (file)
index 0000000..f1a10d2
--- /dev/null
@@ -0,0 +1,6 @@
+request = require 'superagent'
+
+module.exports =
+  'create-tenant':
+    (input, output, done) ->
+      # TODO - this requires OS-KSADM extension
diff --git a/source/spec/promise-intents.coffee b/source/spec/promise-intents.coffee
new file mode 100644 (file)
index 0000000..6ad3ae7
--- /dev/null
@@ -0,0 +1,360 @@
+module.exports =
+  'create-reservation':
+    (input, output, done) ->
+      # 1. create the reservation record (empty)
+      reservation = @create 'ResourceReservation'
+      reservations = @access 'promise.reservations'
+
+      # 2. update the record with requested input
+      reservation.invoke 'update', input.get()
+      .then (res) ->
+        # 3. save the record and add to list
+        res.save()
+        .then ->
+          reservations.push res
+          output.set result: 'ok', message: 'reservation request accepted'
+          output.set 'reservation-id', res.id
+          done()
+        .catch (err) ->
+          output.set result: 'error', message: err
+          done()
+      .catch (err) ->
+        output.set result: 'conflict', message: err
+        done()
+
+  'query-reservation':
+    (input, output, done) ->
+      query = input.get()
+      query.capacity = 'reserved'
+      @invoke 'query-capacity', query
+      .then (res) ->
+        output.set 'reservations', res.get 'collections'
+        output.set 'utilization', res.get 'utilization'
+        done()
+      .catch (e) -> done e
+
+  'update-reservation':
+    (input, output, done) ->
+      # TODO: we shouldn't need this... need to check why leaf mandatory: true not being enforced
+      unless (input.get 'reservation-id')?
+        output.set result: 'error', message: "must provide 'reservation-id' parameter"
+        return done()
+
+      # 1. find the reservation
+      reservation = @find 'ResourceReservation', input.get 'reservation-id'
+      unless reservation?
+        output.set result: 'error', message: 'no reservation found for specified identifier'
+        return done()
+
+      # 2. update the record with requested input
+      reservation.invoke 'update', input.get()
+      .then (res) ->
+        # 3. save the updated record
+        res.save()
+        .then ->
+          output.set result: 'ok', message: 'reservation update successful'
+          done()
+        .catch (err) ->
+          output.set result: 'error', message: err
+          done()
+      .catch (err) ->
+        output.set result: 'conflict', message: err
+        done()
+
+  'cancel-reservation':
+    (input, output, done) ->
+      # 1. find the reservation
+      reservation = @find 'ResourceReservation', input.get 'reservation-id'
+      unless reservation?
+        output.set result: 'error', message: 'no reservation found for specified identifier'
+        return done()
+
+      # 2. destroy all traces of this reservation
+      reservation.destroy()
+      .then =>
+        (@access 'promise.reservations').remove reservation.id
+        output.set 'result', 'ok'
+        output.set 'message', 'reservation canceled'
+        done()
+      .catch (e) ->
+        output.set 'result', 'error'
+        output.set 'message', e
+        done()
+
+  'query-capacity':
+    (input, output, done) ->
+      # 1. we gather up all collections that match the specified window
+      window = input.get 'window'
+      metric = input.get 'capacity'
+
+      collections = switch metric
+        when 'total'     then [ 'ResourcePool' ]
+        when 'reserved'  then [ 'ResourceReservation' ]
+        when 'usage'     then [ 'ResourceAllocation' ]
+        when 'available' then [ 'ResourcePool', 'ResourceReservation', 'ResourceAllocation' ]
+
+      matches = collections.reduce ((a, name) =>
+        res = @find name,
+          start: (value) -> (not window.end?)   or (new Date value) <= (new Date window.end)
+          end:   (value) -> (not window.start?) or (new Date value) >= (new Date window.start)
+          enabled: true
+        a.concat res...
+      ), []
+
+      if window.scope is 'exclusive'
+        # yes, we CAN query filter in one shot above but this makes logic cleaner...
+        matches = matches.where
+          start: (value) -> (not window.start?) or (new Date value) >= (new Date window.start)
+          end:   (value) -> (not window.end?) or (new Date value) <= (new Date window.end)
+
+      # exclude any identifiers specified
+      matches = matches.without id: (input.get 'without')
+
+      if metric is 'available'
+        # excludes allocations with reservation property set (to prevent double count)
+        matches = matches.without reservation: (v) -> v?
+
+      output.set 'collections', matches
+      unless (input.get 'show-utilization') is true
+        return done()
+
+      # 2. we calculate the deltas based on start/end times of each match item
+      deltas = matches.reduce ((a, entry) ->
+        b = entry.get()
+        b.end ?= 'infiniteT'
+        [ skey, ekey ] = [ (b.start.split 'T')[0], (b.end.split 'T')[0] ]
+        a[skey] ?= count: 0, capacity: {}
+        a[ekey] ?= count: 0, capacity: {}
+        a[skey].count += 1
+        a[ekey].count -= 1
+
+        for k, v of b.capacity when v?
+          a[skey].capacity[k] ?= 0
+          a[ekey].capacity[k] ?= 0
+          if entry.name is 'ResourcePool'
+            a[skey].capacity[k] += v
+            a[ekey].capacity[k] -= v
+          else
+            a[skey].capacity[k] -= v
+            a[ekey].capacity[k] += v
+        return a
+      ), {}
+
+      # 3. we then sort the timestamps and aggregate the deltas
+      last = count: 0, capacity: {}
+      usages = for timestamp in Object.keys(deltas).sort() when timestamp isnt 'infinite'
+        entry = deltas[timestamp]
+        entry.timestamp = (new Date timestamp).toJSON()
+        entry.count += last.count
+        for k, v of entry.capacity
+          entry.capacity[k] += (last.capacity[k] ? 0)
+        last = entry
+        entry
+
+      output.set 'utilization', usages
+      done()
+
+  'increase-capacity':
+    (input, output, done) ->
+      pool = @create 'ResourcePool', input.get()
+      pool.save()
+      .then (res) =>
+        (@access 'promise.pools').push res
+        output.set result: 'ok', message: 'capacity increase successful'
+        output.set 'pool-id', res.id
+        done()
+      .catch (e) ->
+        output.set result: 'error', message: e
+        done()
+
+  'decrease-capacity':
+    (input, output, done) ->
+      request = input.get()
+      for k, v of request.capacity
+        request.capacity[k] = -v
+      pool = @create 'ResourcePool', request
+      pool.save()
+      .then (res) =>
+        (@access 'promise.pools').push res
+        output.set result: 'ok', message: 'capacity decrease successful'
+        output.set 'pool-id', res.id
+        done()
+      .catch (e) ->
+        output.set result: 'error', message: e
+        done()
+
+  # TEMPORARY (should go into VIM-specific module)
+  'create-instance':
+    (input, output, done) ->
+      pid = input.get 'provider-id'
+      if pid?
+        provider = @find 'ResourceProvider', pid
+        unless provider?
+          output.set result: 'error', message: "no matching provider found for specified identifier: #{pid}"
+          return done()
+      else
+        provider = (@find 'ResourceProvider')[0]
+        unless provider?
+          output.set result: 'error', message: "no available provider found for create-instance"
+          return done()
+
+      # calculate required capacity based on 'flavor' and other params
+      flavor = provider.access "services.compute.flavors.#{input.get 'flavor'}"
+      unless flavor?
+        output.set result: 'error', message: "no such flavor found for specified identifier: #{pid}"
+        return done()
+
+      required =
+        instances: 1
+        cores:     flavor.get 'vcpus'
+        ram:       flavor.get 'ram'
+        gigabytes: flavor.get 'disk'
+
+      rid = input.get 'reservation-id'
+      if rid?
+        reservation = @find 'ResourceReservation', rid
+        unless reservation?
+          output.set result: 'error', message: 'no valid reservation found for specified identifier'
+          return done()
+        unless (reservation.get 'active') is true
+          output.set result: 'error', message: "reservation is currently not active"
+          return done()
+        available = reservation.get 'remaining'
+      else
+        available = @get 'promise.capacity.available'
+
+      # TODO: need to verify whether 'provider' associated with this 'reservation'
+
+      for k, v of required when v? and !!v
+        unless available[k] >= v
+          output.set result: 'conflict', message: "required #{k}=#{v} exceeds available #{available[k]}"
+          return done()
+
+      @create 'ResourceAllocation',
+        reservation: rid
+        capacity: required
+      .save()
+      .then (instance) =>
+        url = provider.get 'services.compute.endpoint'
+        payload =
+          server:
+            name: input.get 'name'
+            imageRef: input.get 'image'
+            flavorRef: input.get 'flavor'
+        networks = (input.get 'networks').filter (x) -> x? and !!x
+        if networks.length > 0
+          payload.server.networks = networks.map (x) -> uuid: x
+
+        request = @parent.require 'superagent'
+        request
+          .post "#{url}/servers"
+          .send payload
+          .set 'X-Auth-Token', provider.get 'token'
+          .set 'Accept', 'application/json'
+          .end (err, res) =>
+            if err? or !res.ok
+              instance.destroy()
+              #console.error err
+              return done res.error
+            #console.log JSON.stringify res.body, null, 2
+            instance.set 'instance-ref',
+              provider: provider
+              server: res.body.server.id
+            (@access 'promise.allocations').push instance
+            output.set result: 'ok', message: 'create-instance request accepted'
+            output.set 'instance-id', instance.id
+            done()
+         return instance
+      .catch (err) ->
+        output.set result: 'error', mesage: err
+        done()
+
+  'destroy-instance':
+    (input, output, done) ->
+      # 1. find the instance
+      instance = @find 'ResourceAllocation', input.get 'instance-id'
+      unless instance?
+        output.set result: 'error', message: 'no allocation found for specified identifier'
+        return done()
+
+      # 2. destroy all traces of this instance
+      instance.destroy()
+      .then =>
+        # always remove internally
+        (@access 'promise.allocations').remove instance.id
+        ref = instance.get 'instance-ref'
+        provider = (@access "promise.providers.#{ref.provider}")
+        url = provider.get 'services.compute.endpoint'
+        request = @parent.require 'superagent'
+        request
+          .delete "#{url}/servers/#{ref.server}"
+          .set 'X-Auth-Token', provider.get 'token'
+          .set 'Accept', 'application/json'
+          .end (err, res) =>
+            if err? or !res.ok
+              console.error err
+              return done res.error
+            output.set 'result', 'ok'
+            output.set 'message', 'instance destroyed and resource released back to pool'
+            done()
+        return instance
+      .catch (e) ->
+        output.set 'result', 'error'
+        output.set 'message', e
+        done()
+
+  # TEMPORARY (should go into VIM-specific module)
+  'add-provider':
+    (input, output, done) ->
+      app = @parent
+      request = app.require 'superagent'
+
+      payload = switch input.get 'provider-type'
+        when 'openstack'
+          auth:
+            tenantId: input.get 'tenant.id'
+            tenantName: input.get 'tenant.name'
+            passwordCredentials: input.get 'username', 'password'
+
+      unless payload?
+        return done 'Sorry, only openstack supported at this time'
+
+      url = input.get 'endpoint'
+      switch input.get 'strategy'
+        when 'keystone', 'oauth'
+          url += '/tokens' unless /\/tokens$/.test url
+
+      providers = @access 'promise.providers'
+      request
+        .post url
+        .send payload
+        .set 'Accept', 'application/json'
+        .end (err, res) =>
+          if err? or !res.ok then return done res.error
+          #console.log JSON.stringify res.body, null, 2
+          access = res.body.access
+          provider = @create 'ResourceProvider',
+            token: access?.token?.id
+            name: access?.token?.tenant?.name
+          provider.invoke 'update', access.serviceCatalog
+          .then (res) ->
+            res.save()
+            .then ->
+              providers.push res
+              output.set 'result', 'ok'
+              output.set 'provider-id', res.id
+              done()
+            .catch (err) ->
+              output.set 'error', message: err
+              done()
+          .catch (err) ->
+            output.set 'error', message: err
+            done()
+
+      # @using 'mano', ->
+      #   @invoke 'add-provider', (input.get 'endpoint', 'region', 'username', 'password')
+      #   .then (res) =>
+      #     (@access 'promise.providers').push res
+      #     output.set 'result', 'ok'
+      #     output.set 'provider-id', res.id
+      #     done()
diff --git a/source/spec/promise-module.coffee b/source/spec/promise-module.coffee
new file mode 100644 (file)
index 0000000..3eea482
--- /dev/null
@@ -0,0 +1,72 @@
+module.exports =
+  '/opnfv-promise/promise/capacity/total': (prev) ->
+    @computed (->
+      combine = (a, b) ->
+        for k, v of b.capacity when v?
+          a[k] ?= 0
+          a[k] += v
+        return a
+      (@parent.get 'pools')
+      .filter (entry) -> entry.active is true
+      .reduce combine, {}
+    ), type: prev
+
+  '/opnfv-promise/promise/capacity/reserved', (prev) ->
+    @computed (->
+      combine = (a, b) ->
+        for k, v of b.capacity when v?
+          a[k] ?= 0
+          a[k] += v
+        return a
+      (@parent.get 'reservations')
+      .filter (entry) -> entry.active is true
+      .reduce combine, {}
+    ), type: prev
+
+  # rebind to be a computed property
+  '/opnfv-promise/promise/capacity/usage': (prev) ->
+    @computed (->
+      combine = (a, b) ->
+        for k, v of b.capacity when v?
+          a[k] ?= 0
+          a[k] += v
+        return a
+      (@parent.get 'allocations')
+      .filter (entry) -> entry.active is true
+      .reduce combine, {}
+    ), type: prev
+
+  # rebind to be a computed property
+  '/opnfv-promise/promise/capacity/available': (prev) ->
+    @computed (->
+      total = @get 'total'
+      reserved = @get 'reserved'
+      usage = @get 'usage'
+      for k, v of total when v?
+        total[k] -= reserved[k] if reserved[k]?
+        total[k] -= usage[k] if usage[k]?
+      total
+    ), type: prev
+
+  '/opnfv-promise/create-reservation':
+    (input, output, done) ->
+      # 1. create the reservation record (empty)
+      reservation = @create 'ResourceReservation'
+      reservations = @access 'promise.reservations'
+
+      # 2. update the record with requested input
+      reservation.invoke 'update', input.get()
+      .then (res) ->
+        # 3. save the record and add to list
+        res.save()
+        .then ->
+          reservations.push res
+          output.set result: 'ok', message: 'reservation request accepted'
+          output.set 'reservation-id', res.id
+          done()
+        .catch (err) ->
+          output.set result: 'error', message: err
+          done()
+      .catch (err) ->
+        output.set result: 'conflict', message: err
+        done()
diff --git a/source/test/mocha.opts b/source/test/mocha.opts
new file mode 100644 (file)
index 0000000..1e154ee
--- /dev/null
@@ -0,0 +1,2 @@
+--require should
+--compilers coffee:coffee-script/register
diff --git a/source/test/promise-intents.coffee b/source/test/promise-intents.coffee
new file mode 100644 (file)
index 0000000..8e6286b
--- /dev/null
@@ -0,0 +1,437 @@
+config = require 'config'
+assert = require 'assert'
+forge  = require 'yangforge'
+app = forge.load '!yaml ../promise.yaml', async: false, pkgdir: __dirname
+
+# this is javascript promise framework and not related to opnfv-promise
+promise = require 'promise'
+
+if process.env.DEBUG
+  debug = console.log
+else
+  debug = ->
+
+# in the future with YF 0.12.x
+# app = forge.load('..').build('test')
+# app.set config
+# app.use 'proxy', target: x.x.x.x:5050, interface: 'restjson'
+
+describe "promise", ->
+  before ->
+    # ensure we have valid OpenStack environment to test against
+    try
+      config.get 'openstack.auth.endpoint'
+    catch e
+      throw new Error "missing OpenStack environmental variables"
+
+
+  # below 'provider' is used across test suites
+  provider = undefined
+
+  # Test Scenario 00 (FUTURE)
+  # describe "prepare OpenStack for testing", ->
+  #   before (done) ->
+  #     # ensure we have valid OpenStack environment to test against
+  #     try
+  #       config.get 'openstack.auth.url'
+  #     catch e
+  #       throw new Error "missing OpenStack environmental variables"
+
+  #     os = forge.load '!yaml ../openstack.yaml', async: false, pkgdir: __dirname
+  #     app.attach 'openstack', os.access 'openstack'
+  #     app.set config
+
+  #   describe "authenticate", ->
+  #     it "should retrieve available service catalog", (done) ->
+  #       app.access('openstack').invoke 'authenticate'
+  #       .then (res) ->
+
+  #         done()
+  #       .catch (err) -> done err
+
+  #   describe "create-tenant", ->
+  #     # create a new tenant for testing purposes
+
+  #   describe "upload-image", ->
+  #     # upload a new test image
+
+
+
+  # Test Scenario 01
+  describe "register OpenStack into resource pool", ->
+    pool = undefined
+
+    # TC-01
+    describe "add-provider", ->
+      it "should add a new OpenStack provider without error", (done) ->
+        @timeout 5000
+
+        auth = config.get 'openstack.auth'
+        auth['provider-type'] = 'openstack'
+
+        app.access('opnfv-promise').invoke 'add-provider', auth
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          provider = id: res.get('provider-id')
+          # HACK - we delay by a second to allow time for discovering capacity and flavors
+          setTimeout done, 1000
+        .catch (err) -> done err
+
+      it "should update promise.providers with a new entry", ->
+        app.get('opnfv-promise.promise.providers').should.have.length(1)
+
+      it "should contain a new ResourceProvider record in the store", ->
+        assert provider?.id?, "unable to check without ID"
+        provider = app.access('opnfv-promise').find('ResourceProvider', provider.id)
+        assert provider?
+
+    # TC-02
+    describe "increase-capacity", ->
+      it "should add more capacity to the reservation service without error", (done) ->
+        app.access('opnfv-promise').invoke 'increase-capacity',
+          source: provider
+          capacity:
+            cores: 20
+            ram: 51200
+            instances: 10
+            addresses: 10
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          pool = id: res.get('pool-id')
+          done()
+        .catch (err) -> done err
+
+      it "should update promise.pools with a new entry", ->
+        app.get('opnfv-promise.promise.pools').should.have.length(1)
+
+      it "should contain a ResourcePool record in the store", ->
+        assert pool?.id?, "unable to check without ID"
+        pool = app.access('opnfv-promise').find('ResourcePool', pool.id)
+        assert pool?
+
+    # TC-03
+    describe "query-capacity", ->
+      it "should report total collections and utilizations", (done) ->
+        app.access('opnfv-promise').invoke 'query-capacity',
+          capacity: 'total'
+        .then (res) ->
+          res.get('collections').should.be.Array
+          res.get('collections').length.should.be.above(0)
+          res.get('utilization').should.be.Array
+          res.get('utilization').length.should.be.above(0)
+          done()
+        .catch (err) -> done err
+
+      it "should contain newly added capacity pool", (done) ->
+        app.access('opnfv-promise').invoke 'query-capacity',
+          capacity: 'total'
+        .then (res) ->
+          res.get('collections').should.containEql "ResourcePool:#{pool.id}"
+          done()
+        .catch (err) -> done err
+
+  # Test Scenario 02
+  describe "allocation without reservation", ->
+
+    # TC-04
+    describe "create-instance", ->
+      allocation = undefined
+      instance_id = undefined
+
+      before ->
+        # XXX - need to determine image and flavor to use in the given provider for this test
+        assert provider?,
+          "unable to execute without registered 'provider'"
+
+      it "should create a new server in target provider without error", (done) ->
+        @timeout 5000
+        test = config.get 'openstack.test'
+        app.access('opnfv-promise').invoke 'create-instance',
+          'provider-id': provider.id
+          name: 'promise-test-no-reservation'
+          image:   test.image
+          flavor:  test.flavor
+          networks: [ test.network ]
+        .then (res) ->
+          debug res.get()
+          res.get('result').should.equal 'ok'
+          instance_id = res.get('instance-id')
+          done()
+        .catch (err) -> done err
+
+      it "should update promise.allocations with a new entry", ->
+        app.get('opnfv-promise.promise.allocations').length.should.be.above(0)
+
+      it "should contain a new ResourceAllocation record in the store", ->
+        assert instance_id?, "unable to check without ID"
+        allocation = app.access('opnfv-promise').find('ResourceAllocation', instance_id)
+        assert allocation?
+
+      it "should reference the created server ID from the provider", ->
+        assert allocation?, "unable to check without record"
+        allocation.get('instance-ref').should.have.property('provider')
+        allocation.get('instance-ref').should.have.property('server')
+
+      it "should have low priority state", ->
+        assert allocation?, "unable to check without record"
+        allocation.get('priority').should.equal 'low'
+
+  # Test Scenario 03
+  describe "allocation using reservation for immediate use", ->
+    reservation = undefined
+
+    # TC-05
+    describe "create-reservation", ->
+      it "should create reservation record (no start/end) without error", (done) ->
+        app.access('opnfv-promise').invoke 'create-reservation',
+          capacity:
+            cores: 5
+            ram: 25600
+            addresses: 3
+            instances: 3
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          reservation = id: res.get('reservation-id')
+          done()
+        .catch (err) -> done err
+
+      it "should update promise.reservations with a new entry", ->
+        app.get('opnfv-promise.promise.reservations').length.should.be.above(0)
+
+      it "should contain a new ResourceReservation record in the store", ->
+        assert reservation?.id?, "unable to check without ID"
+        reservation = app.access('opnfv-promise').find('ResourceReservation', reservation.id)
+        assert reservation?
+
+    # TC-06
+    describe "create-instance", ->
+      allocation = undefined
+
+      before ->
+        assert provider?,
+          "unable to execute without registered 'provider'"
+        assert reservation?,
+          "unable to execute without valid reservation record"
+
+      it "should create a new server in target provider (with reservation) without error", (done) ->
+        @timeout 5000
+        test = config.get 'openstack.test'
+        app.access('opnfv-promise').invoke 'create-instance',
+          'provider-id': provider.id
+          name: 'promise-test-reservation'
+          image:  test.image
+          flavor: test.flavor
+          networks: [ test.network ]
+          'reservation-id': reservation.id
+        .then (res) ->
+          debug res.get()
+          res.get('result').should.equal 'ok'
+          allocation = id: res.get('instance-id')
+          done()
+        .catch (err) -> done err
+
+      it "should contain a new ResourceAllocation record in the store", ->
+        assert allocation?.id?, "unable to check without ID"
+        allocation = app.access('opnfv-promise').find('ResourceAllocation', allocation.id)
+        assert allocation?
+
+      it "should be referenced in the reservation record", ->
+        assert reservation? and allocation?, "unable to check without records"
+        reservation.get('allocations').should.containEql allocation.id
+
+      it "should have high priority state", ->
+        assert allocation?, "unable to check without record"
+        allocation.get('priority').should.equal 'high'
+
+  # Test Scenario 04
+  describe "reservation for future use", ->
+    reservation = undefined
+    start = new Date
+    end   = new Date
+    # 7 days in the future
+    start.setTime (start.getTime() + 7*60*60*1000)
+    # 8 days in the future
+    end.setTime (end.getTime() + 8*60*60*1000)
+
+    # TC-07
+    describe "create-reservation", ->
+      it "should create reservation record (for future) without error", (done) ->
+        app.access('opnfv-promise').invoke 'create-reservation',
+          start: start.toJSON()
+          end: end.toJSON()
+          capacity:
+            cores: 1
+            ram: 12800
+            addresses: 1
+            instances: 1
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          reservation = id: res.get('reservation-id')
+          done()
+        .catch (err) -> done err
+
+      it "should update promise.reservations with a new entry", ->
+        app.get('opnfv-promise.promise.reservations').length.should.be.above(0)
+
+      it "should contain a new ResourceReservation record in the store", ->
+        assert reservation?.id?, "unable to check without ID"
+        reservation = app.access('opnfv-promise').find('ResourceReservation', reservation.id)
+        assert reservation?
+
+    # TC-08
+    describe "query-reservation", ->
+      it "should contain newly created future reservation", (done) ->
+        app.access('opnfv-promise').invoke 'query-reservation',
+          window:
+            start: start.toJSON()
+            end: end.toJSON()
+        .then (res) ->
+          res.get('reservations').should.containEql reservation.id
+          done()
+        .catch (err) -> done err
+
+    # TC-09
+    describe "update-reservation", ->
+      it "should modify existing reservation without error", (done) ->
+        app.access('opnfv-promise').invoke 'update-reservation',
+          'reservation-id': reservation.id
+          capacity:
+            cores: 3
+            ram: 12800
+            addresses: 2
+            instances: 2
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          done()
+        .catch (err) -> done err
+
+    # TC-10
+    describe "cancel-reservation", ->
+      it "should modify existing reservation without error", (done) ->
+        app.access('opnfv-promise').invoke 'cancel-reservation',
+          'reservation-id': reservation.id
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          done()
+        .catch (err) -> done err
+
+      it "should no longer contain record of the deleted reservation", ->
+        assert reservation?.id?, "unable to check without ID"
+        reservation = app.access('opnfv-promise').find('ResourceReservation', reservation.id)
+        assert not reservation?
+
+  # Test Scenario 05
+  describe "capacity planning", ->
+
+    # TC-11
+    describe "decrease-capacity", ->
+      start = new Date
+      end   = new Date
+      # 30 days in the future
+      start.setTime (start.getTime() + 30*60*60*1000)
+      # 45 days in the future
+      end.setTime (end.getTime() + 45*60*60*1000)
+
+      it "should decrease available capacity from a provider in the future", (done) ->
+        app.access('opnfv-promise').invoke 'decrease-capacity',
+          source: provider
+          capacity:
+            cores: 5
+            ram: 17920
+            instances: 5
+          start: start.toJSON()
+          end: end.toJSON()
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          done()
+        .catch (err) -> done err
+
+    # TC-12
+    describe "increase-capacity", ->
+      start = new Date
+      end   = new Date
+      # 14 days in the future
+      start.setTime (start.getTime() + 14*60*60*1000)
+      # 21 days in the future
+      end.setTime (end.getTime() + 21*60*60*1000)
+
+      it "should increase available capacity from a provider in the future", (done) ->
+        app.access('opnfv-promise').invoke 'decrease-capacity',
+          source: provider
+          capacity:
+            cores: 1
+            ram: 3584
+            instances: 1
+          start: start.toJSON()
+          end: end.toJSON()
+        .then (res) ->
+          res.get('result').should.equal 'ok'
+          done()
+        .catch (err) -> done err
+
+    # TC-13 (Should improve this TC)
+    describe "query-capacity", ->
+      it "should report available collections and utilizations", (done) ->
+        app.access('opnfv-promise').invoke 'query-capacity',
+          capacity: 'available'
+        .then (res) ->
+          res.get('collections').should.be.Array
+          res.get('collections').length.should.be.above(0)
+          res.get('utilization').should.be.Array
+          res.get('utilization').length.should.be.above(0)
+          done()
+        .catch (err) -> done err
+
+  # Test Scenario 06
+  describe "reservation with conflict", ->
+    # TC-14
+    describe "create-reservation", ->
+      it "should fail to create immediate reservation record with proper error", (done) ->
+        app.access('opnfv-promise').invoke 'create-reservation',
+          capacity:
+            cores: 5
+            ram: 17920
+            instances: 10
+        .then (res) ->
+          res.get('result').should.equal 'conflict'
+          done()
+        .catch (err) -> done err
+
+      it "should fail to create future reservation record with proper error", (done) ->
+        start = new Date
+        # 30 days in the future
+        start.setTime (start.getTime() + 30*60*60*1000)
+
+        app.access('opnfv-promise').invoke 'create-reservation',
+          capacity:
+            cores: 5
+            ram: 17920
+            instances: 10
+          start: start.toJSON()
+        .then (res) ->
+          res.get('result').should.equal 'conflict'
+          done()
+        .catch (err) -> done err
+
+  # Test Scenario 07
+  describe "cleanup test allocations", ->
+    allocations = undefined
+    before ->
+      allocations = app.get('opnfv-promise.promise.allocations')
+      debug provider.get()
+      debug allocations
+      allocations.length.should.be.above(0)
+
+    describe "destroy-instance", ->
+      it "should successfully destroy all allocations", (done) ->
+        @timeout 5000
+        promises = allocations.map (x) ->
+          app.access('opnfv-promise').invoke 'destroy-instance',
+            'instance-id': x.id
+        promise.all promises
+        .then (res) ->
+          res.forEach (x) ->
+            debug x.get()
+            x.get('result').should.equal 'ok'
+          done()
+        .catch (err) -> done err