98dcdc47e6d9525ab9b0a33f1eb638fc4b3196b4
[promise.git] / deprecated / source / promise.yaml
1 #
2 # Author: Peter K. Lee (peter@corenova.com)
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9
10 name: opnfv-promise
11 description: Resource Management for Virtualized Infrastructure
12 author: Peter K. Lee <peter@corenova.com>
13 license: Apache-2.0
14 homepage: http://wiki.opnfv.org/promise
15 repository: git://github.com/opnfv/promise.git
16 yangforge: "0.11.x"
17 keywords:
18   - opnfv
19   - promise
20   - vim
21   - nfvi
22   - infrastructure
23   - openstack
24   - nbi
25   - yangforge
26   - resource
27   - reservation
28   - capacity
29   - allocation
30
31 schema: !yang schema/opnfv-promise.yang
32
33 # below config provides default parameters
34 # NOTE: uncomment locally for testing with pre-existing data
35 #config: !json config/demo.json
36
37 dependencies:
38   access-control-models: !yang schema/access-control-models.yang
39   nfv-infrastructure:    !yang schema/nfv-infrastructure.yang
40
41 # MODULE model active bindings
42 module:
43   opnfv-promise:
44     # rebind to be a computed property
45     promise.capacity.total: !coffee/function |
46       (prev) -> @computed (->
47         combine = (a, b) ->
48           for k, v of b.capacity when v?
49             a[k] ?= 0
50             a[k] += v
51           return a
52         (@parent.get 'pools')
53         .filter (entry) -> entry.active is true
54         .reduce combine, {}
55       ), type: prev
56     # rebind to be a computed property
57     promise.capacity.reserved: !coffee/function |
58       (prev) -> @computed (->
59         combine = (a, b) ->
60           for k, v of b.remaining when v?
61             a[k] ?= 0
62             a[k] += v
63           return a
64         (@parent.get 'reservations')
65         .filter (entry) -> entry.active is true
66         .reduce combine, {}
67       ), type: prev
68     # rebind to be a computed property
69     promise.capacity.usage: !coffee/function |
70       (prev) -> @computed (->
71         combine = (a, b) ->
72           for k, v of b.capacity when v?
73             a[k] ?= 0
74             a[k] += v
75           return a
76         (@parent.get 'allocations')
77         .filter (entry) -> entry.active is true
78         .reduce combine, {}
79       ), type: prev
80     # rebind to be a computed property
81     promise.capacity.available: !coffee/function |
82       (prev) -> @computed (->
83         total = @get 'total'
84         reserved = @get 'reserved'
85         usage = @get 'usage'
86         for k, v of total when v?
87           total[k] -= reserved[k] if reserved[k]?
88           total[k] -= usage[k] if usage[k]?
89         total
90       ), type: prev
91
92 # RPC definitions (INTENT interfaces)
93 rpc: !require spec/promise-intents.coffee
94
95 # COMPLEX-TYPE model active bindings (controller logic)
96 complex-type:
97   ResourceElement:
98     #properties
99     id: !coffee/function |
100       (prev) -> prev.set 'default', -> @uuid()
101
102   ResourceCollection:
103     # properties
104     start: !coffee/function |
105       (prev) -> prev.set 'default', -> (new Date).toJSON()
106
107     active: !coffee/function |
108       (prev) -> @computed (->
109         now = new Date
110         start = new Date (@get 'start')
111         end = switch
112           when (@get 'end')? then new Date (@get 'end')
113           else now
114         (@get 'enabled') and (start <= now <= end)
115       ), type: prev
116
117   ResourceReservation:
118     end: !coffee/function |
119       (prev) -> prev.set 'default', ->
120         end = (new Date @get 'start')
121         max = @parent.get 'promise.policy.reservation.max-duration'
122         return unless max?
123         end.setTime (end.getTime() + (max*60*60*1000))
124         end.toJSON()
125
126     allocations: !coffee/function |
127       (prev) -> @computed (->
128         res = (@store.find 'ResourceAllocation', reservation: @id)
129         res.map (x) -> x.get 'id'
130       ), type: 'array'
131
132     remaining: !coffee/function |
133       (prev) -> @computed (->
134         total = @get 'capacity'
135         records = @store.find 'ResourceAllocation', id: (@get 'allocations'), active: true
136         for entry in records
137           usage = entry.get 'capacity'
138           for k, v of usage
139             total[k] -= v
140         total
141       ), type: prev
142
143     # methods
144     validate: !coffee/function |
145       (prev) -> (value={}, resolve, reject) ->
146         # validate that request contains sufficient data
147         for k, v of value.capacity when v? and !!v
148           hasCapacity = true
149         if (not hasCapacity) and value.elements.length is 0
150           return reject "unable to validate reservation record without anything being reserved"
151         # time range verifications
152         now = new Date
153         start = (new Date value.start) if value.start?
154         end   = (new Date value.end) if value.end?
155         # if start? and start < now
156         #   return reject "requested start time #{value.start} cannot be in the past"
157         if end? and end < now
158           return reject "requested end time #{value.end} cannot be in the past"
159         if start? and end? and start > end
160           retun reject "requested start time must be earlier than end time"
161         resolve this
162
163     update: !coffee/function |
164       (prev) -> (req, resolve, reject) ->
165         req.start ?= @get 'start'
166         req.end   ?= @get 'end'
167
168         # TODO: should validate here...
169         @parent.invoke 'query-capacity',
170           start: req.start
171           end: req.end
172           capacity: 'available'
173           without: @id
174         .then (res) =>
175           collections = res.get 'collections'
176           unless collections.length > 0
177             return reject 'no resource capacity available during requested start/end time'
178
179           pools = collections.filter (e) -> /^ResourcePool/.test e
180           # should do some policy or check to see if more than one pool acceptable to reservee
181
182           entries = res.get 'utilization'
183           start = new Date req.start
184           end   = new Date req.end
185
186           for x in [0..entries.length-1]
187             t1 = new Date entries[x].timestamp
188             break unless t1 < end
189
190             if x < entries.length-1
191               t2 = new Date entries[x+1].timestamp
192               continue unless t2 > start
193
194             available = entries[x].capacity
195             for k, v of req.capacity when v? and !!v
196               unless available[k] >= v
197                 return reject "requested #{k}=#{v} exceeds available #{available[k]} between #{t1} and #{t2}"
198
199           @set req
200           @set 'pools', pools
201           resolve this
202         .catch (err) -> reject err
203
204     save: !coffee/function |
205       (prev) -> (resolve, reject) ->
206         @invoke 'validate', @get()
207         .then (res) ->
208           # should do something about this reservation record...
209           now = (new Date).toJSON()
210           unless (res.get 'created-on')?
211             res.set 'created-on', now
212           res.set 'modified-on', now
213           resolve res
214         .catch (e) -> reject e
215
216   ResourceAllocation:
217     # properties
218     priority: !coffee/function |
219       (prev) -> @computed (->
220         switch
221           when not (@get 'reservation')? then 3
222           when not (@get 'active') then 2
223           else 1
224       ), type: prev
225
226   ResourcePool:
227     save: !coffee/function |
228       (prev) -> (resolve, reject) ->
229         # validate that record contains sufficient data
230         value = @get()
231
232         for k, v of value.capacity when v? and !!v
233           hasCapacity = true
234         if (not hasCapacity) and value.elements.length is 0
235           return reject "unable to save pool record without any capacity values"
236
237         resolve this
238
239   ResourceProvider:
240     # properties
241     token: !coffee/function |
242       (prev) -> prev.set 'private', true
243
244     pools: !coffee/function |
245       (prev) -> @computed (->
246         (@store.find 'ResourcePool', source: (@get 'name')).map (x) -> x.get 'id'
247       ), type: 'array'
248
249     # methods
250     # XXX - this method is OpenStack-specific only, will need to revise later
251     update: !coffee/function |
252       (prev) -> (services=[], resolve, reject) ->
253         return reject "unable to update provider without list of services" unless services.length
254         request = @store.parent.require 'superagent'
255         services.forEach (service) =>
256           switch service.type
257             when 'compute'
258               url = service.endpoints[0].url
259               @set 'services.compute.endpoint', url
260               request
261                 .get "#{url}/limits"
262                   .set 'X-Auth-Token', @get 'token'
263                   .set 'Accept', 'application/json'
264                   .end (err, res) =>
265                     if err? or !res.ok
266                       console.warn "request to discover capacity limits failed"
267                       return
268
269                     capacity = res.body.limits?.absolute
270                     #console.log "\ndiscovered capacity:"
271                     #console.log capacity
272
273                     (@access 'capacity').set {
274                       cores: capacity.maxTotalCores
275                       ram: capacity.maxTotalRAMSize
276                       instances: capacity.maxTotalInstances
277                       addresses: capacity.maxTotalFloatingIps
278                     }
279               request
280                 .get "#{url}/flavors/detail"
281                   .set 'X-Auth-Token', @get 'token'
282                   .set 'Accept', 'application/json'
283                   .end (err, res) =>
284                     if err? or !res.ok
285                       console.warn "request to discover compute flavors failed"
286                       return
287
288                     flavors = res.body.flavors
289                     # console.log "\ndiscovered flavors:"
290                     # console.log flavors
291                     try
292                       flavors = flavors.map (x) -> ResourceFlavor: x
293                       (@access 'services.compute.flavors').push flavors...
294                     catch er
295                       console.warn "failed to update flavors into the provider due to validation errors"
296
297         # XXX - update should do promise.all
298         resolve this
299