summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/.gitignore16
-rw-r--r--source/.npmignore2
-rw-r--r--source/LICENSE202
-rw-r--r--source/README.md144
-rw-r--r--source/config/custom-environment-variables.yaml23
-rw-r--r--source/config/default.yaml11
-rw-r--r--source/config/demo.json118
-rw-r--r--source/config/functest.yaml9
-rw-r--r--source/config/test-intercloud.yaml21
-rw-r--r--source/forge.yaml42
-rw-r--r--source/index.yaml4120
-rw-r--r--source/openstack.yaml13
-rw-r--r--source/package.json41
-rw-r--r--source/promise.yaml290
-rw-r--r--source/schema/access-control-models.yang92
-rw-r--r--source/schema/nfv-infrastructure.yang322
-rw-r--r--source/schema/nfv-mano.yang149
-rw-r--r--source/schema/openstack-compute.yang72
-rw-r--r--source/schema/openstack-identity.yang84
-rw-r--r--source/schema/openstack.yang74
-rw-r--r--source/schema/opnfv-promise.yang640
-rw-r--r--source/spec/openstack-intents.coffee6
-rw-r--r--source/spec/promise-intents.coffee360
-rw-r--r--source/spec/promise-module.coffee72
-rw-r--r--source/test/mocha.opts2
-rw-r--r--source/test/promise-intents.coffee437
26 files changed, 7362 insertions, 0 deletions
diff --git a/source/.gitignore b/source/.gitignore
new file mode 100644
index 0000000..f49cc53
--- /dev/null
+++ b/source/.gitignore
@@ -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
index 0000000..4f726b0
--- /dev/null
+++ b/source/.npmignore
@@ -0,0 +1,2 @@
+.git*
+
diff --git a/source/LICENSE b/source/LICENSE
new file mode 100644
index 0000000..8f71f43
--- /dev/null
+++ b/source/LICENSE
@@ -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
index 0000000..681e2b9
--- /dev/null
+++ b/source/README.md
@@ -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
index 0000000..30a72d2
--- /dev/null
+++ b/source/config/custom-environment-variables.yaml
@@ -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
index 0000000..52bb61a
--- /dev/null
+++ b/source/config/default.yaml
@@ -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
index 0000000..dffb3af
--- /dev/null
+++ b/source/config/demo.json
@@ -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
index 0000000..8cc84d3
--- /dev/null
+++ b/source/config/functest.yaml
@@ -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
index 0000000..f5e04ed
--- /dev/null
+++ b/source/config/test-intercloud.yaml
@@ -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
index 0000000..24317d1
--- /dev/null
+++ b/source/forge.yaml
@@ -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
index 0000000..071d685
--- /dev/null
+++ b/source/index.yaml
@@ -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
index 0000000..6881186
--- /dev/null
+++ b/source/openstack.yaml
@@ -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
index 0000000..7ae3977
--- /dev/null
+++ b/source/package.json
@@ -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
index 0000000..125c7b7
--- /dev/null
+++ b/source/promise.yaml
@@ -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
index 0000000..7b4684c
--- /dev/null
+++ b/source/schema/access-control-models.yang
@@ -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
index 0000000..ccad269
--- /dev/null
+++ b/source/schema/nfv-infrastructure.yang
@@ -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
index 0000000..0e3bbbe
--- /dev/null
+++ b/source/schema/nfv-mano.yang
@@ -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
index 0000000..c3e790c
--- /dev/null
+++ b/source/schema/openstack-compute.yang
@@ -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
index 0000000..4b92957
--- /dev/null
+++ b/source/schema/openstack-identity.yang
@@ -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
index 0000000..6878f7e
--- /dev/null
+++ b/source/schema/openstack.yang
@@ -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
index 0000000..b606382
--- /dev/null
+++ b/source/schema/opnfv-promise.yang
@@ -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
index 0000000..f1a10d2
--- /dev/null
+++ b/source/spec/openstack-intents.coffee
@@ -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
index 0000000..6ad3ae7
--- /dev/null
+++ b/source/spec/promise-intents.coffee
@@ -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
index 0000000..3eea482
--- /dev/null
+++ b/source/spec/promise-module.coffee
@@ -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
index 0000000..1e154ee
--- /dev/null
+++ b/source/test/mocha.opts
@@ -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
index 0000000..8e6286b
--- /dev/null
+++ b/source/test/promise-intents.coffee
@@ -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