diff options
Diffstat (limited to 'source/promise.yaml')
-rw-r--r-- | source/promise.yaml | 290 |
1 files changed, 290 insertions, 0 deletions
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 + |