Proposal to move Promise shim-layer code to a new deprecated folder
[promise.git] / deprecated / source / promise.yaml
diff --git a/deprecated/source/promise.yaml b/deprecated/source/promise.yaml
new file mode 100644 (file)
index 0000000..98dcdc4
--- /dev/null
@@ -0,0 +1,299 @@
+#
+# Author: Peter K. Lee (peter@corenova.com)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+name: opnfv-promise
+description: Resource Management for Virtualized Infrastructure
+author: Peter K. Lee <peter@corenova.com>
+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].url
+              @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
+