54 | | A key feature desired by experimenters, but not yet included in the Aggregate Manager API, is the ability to update a reservation in place, without losing the existing reservation. At times, this has been called !UpdateSlivers. There is not currently an acceptable proposal for this feature that has been circulated. |
| 54 | == Change Set C: `Update()` == |
| 55 | Add an ability for experimenters to modify their allocated resources at an aggregate without deleting (and possibly losing) existing resource allocations. |
| 56 | |
| 57 | This change was briefly discussed at GEC13, discussed at the GEC14 coding sprint, and is a topic for ongoing discussion. |
| 58 | |
| 59 | === Motivation === |
| 60 | A common complaint among experimenters about the current AM API is that there is no way to atomically add/remove and modify resources at an aggregate. AM API v3 allows aggregates to support adding or deleting some resources from the slice at the aggregate, but not all aggregates can support that without the ability to also modify other resources. For example, you cannot add a link without adding an interface to one or more nodes, and there is no way to modify the node without first deleting it. This proposal aims to address that, by introducing a method to update the slice at an aggregate. |
| 61 | |
| 62 | The [http://svn.planet-lab.org/attachment/wiki/WikiStart/sfa.pdf SFA] calls for an !UpdateSlice method, "to request that additional resources—as specified in the RSpec—be allocated to the slice". |
| 63 | |
| 64 | In the !PlanetLab implementation of the SFA, !UpdateSliver is in fact a synonym for !CreateSliver - the server will ensure that your allocated resources match your request RSpec, adding, removing and modifying resources as needed. It immediately allocates and boots nodes to match the request RSpec. |
| 65 | |
| 66 | The ProtoGENI CMV2 API has [http://www.protogeni.net/trac/protogeni/wiki/ComponentManagerAPIV2#UpdateSliver UpdateSliver], which is described as the way to "Request a change of resuorces for an existing sliver. The new set of resources that are desired are specified in the rspec." At ProtoGENI as at !PlanetLab, this method takes the full RSpec description of resources the experimenter wants, and the server computes the difference with what the experimenter already has. At ProtoGENI though, this method returns a ticket. The experimenter must then redeem the ticket to actually acquire the resources. |
| 67 | |
| 68 | This topic was discussed at the [http://groups.geni.net/geni/wiki/GEC12GeniAmAPI GEC12 AM API session] and on the GENI dev mailing list (in [http://lists.geni.net/pipermail/dev/2011-October/000433.html October] and [http://lists.geni.net/pipermail/dev/2011-November/000531.html November]). |
| 69 | |
| 70 | This topic was also discussed on the geni-dev mailing list here: http://lists.geni.net/pipermail/dev/2012-March/000588.html and here: http://lists.geni.net/pipermail/dev/2012-March/000643.html and elsewhere in March 2012. |
| 71 | |
| 72 | A competing related proposal from Gary Wong is here: http://www.protogeni.net/trac/protogeni/wiki/AM_API_proposals |
| 73 | |
| 74 | Jon Duerig [http://lists.geni.net/pipermail/dev/2012-July/000823.html made a proposal prior to GEC14], which was [http://groups.geni.net/geni/wiki/GEC14Agenda/CodingSprintAndExperimenterTutoring#Update discussed at the coding sprint]. This proposal is a revised version of that proposal. |
| 75 | |
| 76 | === Proposal Summary === |
| 77 | This proposal adds an `Update()` method, a `Cancel()` method, and a new `geni_allocation_state`: `geni_updating`. The proposal includes a new state guarantee RSpec extension, a new `geni_cancelled` option to `Describe()`, and calls for some minor changes to GENI v3 RSpecs. |
| 78 | |
| 79 | === `Update()` === |
| 80 | |
| 81 | Begins a transaction to modify resources which are currently in the `geni_allocated` or `geni_provisioned` state. `Update()` itself changes only the internal allocation of slivers, not their operational state. It is therefore fast and synchronous. |
| 82 | |
| 83 | {{{ |
| 84 | struct Update(string urns[], struct credentials[], struct geni.rspec rspec, struct options) |
| 85 | }}} |
| 86 | |
| 87 | This call accepts the `geni_best_effort` option; when supplied, the aggregate should attempt to partially satisfy the request. |
| 88 | |
| 89 | This call accepts the `geni_end_time` option; when supplied, the aggregate may attempt to renew the slivers being created or modified to the requested time, according to aggregate-local policy. |
| 90 | |
| 91 | Since `Update()` changes the allocation state only, it has an immediate effect on slivers currently in the `geni_allocated` or `geni_updating` states. However, updating slivers in the `geni_provisioned` state is a |
| 92 | two-step process involving first changing the allocation via `Update()` and then committing those changes with `Provision()`. |
| 93 | |
| 94 | The input RSpec is a complete request specification for all slivers included in the `urns` list, as you would like the slivers to exist when the call is fully committed. Any difference in the update RSpec from the current slivers shall be interpreted as a modification request, including removing interfaces or links, or deleting entire slivers. |
| 95 | |
| 96 | The RSpec argument to `Update()` should be a request RSpec, but RSpecs passing the manifest schema are allowed. Resources in the RSpec should include the `sliver_id` tag when the experimenter is requesting |
| 97 | a modification to the given resource. Resources without a `sliver_id` are to be interpreted as new resource requests. |
| 98 | |
| 99 | If a sliver is defined in the RSpec ''and'' contained in the `urns` list, (either by URN or by indirect inclusion because the slice URN is in the `urns` list), then the user is requesting that the sliver be modified. If the sliver was |
| 100 | in the `geni_allocated` state, the allocation of that sliver is changed immediately and the sliver remains in a `geni_allocated` state. If the sliver was in the `geni_provisioned` state, the operational state of |
| 101 | that sliver is preserved and it is placed in the `geni_updating` state where it can be `Provision()`ed to implement the modification or `Cancel()`led. |
| 102 | |
| 103 | If the RSpec does not define one or more slivers which are specified in the `urns` list (that is, does not include resources containing the given `sliver_id` tag), then the user is requesting that the given sliver(s) should be |
| 104 | deleted. If the sliver was in the `geni_allocated` state, the allocated sliver is deleted and the sliver ceases to exist (becomes `geni_unallocated`). If the sliver was in the `geni_provisioned` state, then the operational state of that sliver is preserved and it is placed in the `geni_updating` allocation state where it can be `Provision()`ed to delete the sliver or `Cancel()`led to preserve it. |
| 105 | |
| 106 | Any slivers which are not defined in the RSpec ''and'' do not appear in the `urns` list will remain unchanged. They remain in their current allocation state. |
| 107 | |
| 108 | Any resources requested in the RSpec that are not specified in the `urns` list will be instantiated and be in the `geni_allocated` state, if the aggregate has the resources available. |
| 109 | |
| 110 | === After a successful `Update()` === |
| 111 | |
| 112 | After a successful call to `Update()`, all slivers in the `urns` list which began in the `geni_provisioned` state will be in the `geni_updating` state (and their configuration will revert in the case of a `Cancel()` call described below). This ''includes'' those slivers which are marked for deletion as described above. These slivers will not actually be deleted until the `Provision()` call. |
| 113 | |
| 114 | Slivers which were `geni_provisioned` and are now `geni_updating` will retain their prior existing expiration time. If the expiration time is reached before the sliver is `Provision()`ed, then the resources are |
| 115 | freed and the sliver is `geni_unallocated`. The sliver does not return to the `geni_provisioned` state on sliver expiration. |
| 116 | |
| 117 | All slivers which began in the `geni_allocated` state will remain in the `geni_allocated` state, or will have been deleted and there will be no way (except for attempting another `Update()`) to revert their |
| 118 | configuration. Slivers that are updated and remain in the `geni_allocated` state will have a new expiration time, as determined by the aggregate, but typically set just as for new slivers returned by `Allocate()`. As with other `geni_allocated` slivers, if the sliver expires before it is `Provision()`ed, then the resources are freed and the sliver is `geni_unallocated`. |
| 119 | |
| 120 | Any slivers which began in the `geni_updating` state will remain in the `geni_updating` state, with their prior existing expiration time. If the expiration time is reached before the sliver is `Provision()`ed, then the resources are freed and the sliver is `geni_unallocated`. The sliver does not return to the `geni_provisioned` state on sliver expiration. |
| 121 | |
| 122 | Any new resources requested in the RSpec not associated with a current sliver will be in the `geni_allocated` state and will be added to slivers, just as after a call to `Allocate()`. |
| 123 | |
| 124 | ==== Return ==== |
| 125 | On success, the `value` field of the [wiki:GAPI_AM_API_V3/CommonConcepts#ReturnStruct return struct] will contain a struct: |
| 126 | {{{ |
| 127 | { |
| 128 | geni_rspec: <geni.rspec manifest of newly allocated slivers>, |
| 129 | geni_slivers: [ |
| 130 | { |
| 131 | geni_sliver_urn: <string sliver urn> |
| 132 | geni_expires: <dateTime.rfc3339 allocation expiration string, as in geni_expires from Status>, |
| 133 | geni_allocation_status: <string sliver state - e.g. geni_allocated, geni_updating>, |
| 134 | geni_error: <optional string, may be omitted entirely, explaining any failure for a sliver> |
| 135 | }, |
| 136 | ... |
| 137 | ] |
| 138 | } |
| 139 | }}} |
| 140 | |
| 141 | The list of returned slivers includes even those that will be deleted. |
| 142 | |
| 143 | === State Guarantees === |
| 144 | |
| 145 | The manifest RSpec may include an extension describing what guarantees the AM will make about state preservation. Omitting the extension means the AM provides no guarantees. It is proposed that the extension have the following RNC schema: |
| 146 | |
| 147 | {{{ |
| 148 | default namespace = "http://www.geni.net/resources/rspec/ext/preserve/1" |
| 149 | |
| 150 | # This is meant to extend a node or link |
| 151 | Preserved = element preserve { |
| 152 | attribute guarantee { "geni_none" | "geni_persistent_state" | |
| 153 | "geni_dynamic_state" | "geni_no_disruption" } |
| 154 | } |
| 155 | |
| 156 | start = Preserved |
| 157 | }}} |
| 158 | |
| 159 | Guarantee levels are defined to mean: |
| 160 | * `geni_none` is returned when the AM provides no state preservation guarantees for a sliver. |
| 161 | * `geni_persistent_state` is returned when the AM may wipe out dynamic state (i.e. reboot a node) but will preserve persistent state. |
| 162 | * `geni_dynamic_state` is returned when the AM retains the dynamic state but there may be other perturbations such as packet loss or service restarts. |
| 163 | * `geni_no_disruption` is returned when the AM will not make any changes that can perturb the sliver. |
| 164 | |
| 165 | === `Cancel()` === |
| 166 | |
| 167 | Cancels an `Update()` or an `Allocate()`. |
| 168 | |
| 169 | {{{ |
| 170 | struct Cancel(string urns[], string credentials[], struct options) |
| 171 | }}} |
| 172 | |
| 173 | This call accepts the `geni_best_effort` option; as with `Delete()`, then this option is supplied the aggregate should attempt to de-allocate individual slivers. |
| 174 | |
| 175 | When applied to slivers in the `geni_allocated` state, `Cancel()` acts just like `Delete()`. When applied to slivers in the `geni_updating` state, those slivers are returned to the `geni_provisioned` state with no change to the |
| 176 | operational state or attributes of the existing slivers. |
| 177 | |
| 178 | Return: On success, the `value` field of the [wiki:GAPI_AM_API_V3/CommonConcepts#ReturnStruct return struct] will contain a struct: |
| 179 | {{{ |
| 180 | { |
| 181 | geni_rspec: <geni.rspec, a Manifest RSpec> |
| 182 | geni_urn: <string slice urn of the containing slice> |
| 183 | geni_slivers: [ |
| 184 | { |
| 185 | geni_sliver_urn: <string sliver urn> |
| 186 | geni_expires: <dateTime.rfc3339 allocation expiration string, as in geni_expires from SliversStatus>, |
| 187 | geni_allocation_status: <string sliver state - e.g. geni_allocated or geni_provisioned >, |
| 188 | geni_operational_status: <string sliver operational state>, |
| 189 | geni_error: <optional string, may be omitted entirely, explaining any failure for a sliver> |
| 190 | }, |
| 191 | ... |
| 192 | ] |
| 193 | } |
| 194 | }}} |
| 195 | |
| 196 | === Changes to `Provision()` === |
| 197 | |
| 198 | When applied to slivers in the `geni_updating` state, this call will either remove those slivers (if they were marked for deletion as described in the `Update()` call) or move them to the `geni_provisioned` |
| 199 | state, provisioning them according to the manifest RSpec returned from that call. |
| 200 | |
| 201 | === Changes to `Describe()` === |
| 202 | |
| 203 | The addition of an `Update()` operation creates an ambiguity on the slivers being described. Should the Aggregate `Describe()` the slivers as they will |
| 204 | be instantiated on a successful `Provision()` (including all slivers that are `geni_allocated`, `geni_updating`, and `geni_provisioned`)? Or should the AM describe the |
| 205 | slivers which would remain after a `Cancel()` (including only the slivers that are `geni_allocated` or `geni_provisioned`, plus the updated slivers as they existed before being updated)? |
| 206 | |
| 207 | In order to account for this ambiguity, `Describe()` accepts a new option, `geni_cancelled` which defaults to `false`. When `geni_cancelled` is `true`, |
| 208 | `Describe()` returns the state of all currently `geni_provisioned` slivers, showing the user what their slice will look like if they cancel all `geni_updating` and `geni_allocated` slivers. When `geni_cancelled` is `false`, the manifest and status will reflect the changed slivers in the `geni_updating` and `geni_allocated` states. |
| 209 | |
| 210 | === Changes to `Delete()` === |
| 211 | |
| 212 | A call to `Delete()` can be used to delete slivers in any state. |
| 213 | |
| 214 | === Change to `geni_single_allocation` === |
| 215 | |
| 216 | When an AM reports `geni_single_allocation` as `true` in `GetVersion()`, the following additional restrictions apply to calls: |
| 217 | |
| 218 | * When calling `Update()`, the `urns` list must either contain a slice URN or it must contain the URN of every sliver at this Aggregate Manager in the given slice. |
| 219 | * When calling `Provision()`, the `urns` list must contain the sliver URNs of all slivers in either the `geni_allocated` or `geni_updating` states in the given slice. |
| 220 | * When calling `Cancel()`, the `urns` list must contain the sliver URNs of all slivers in either the `geni_allocated` or `geni_updating` states in the given slice. |
| 221 | |
| 222 | === Mixed State Calls === |
| 223 | |
| 224 | A `Renew()` call which contains sliver URNs in multiple allocation states will attempt to renew them all to the same requested expiration time. |
| 225 | |
| 226 | A `Delete()` call which contains sliver URNs in multiple allocation states will attempt to delete all slivers and put them in the `geni_unallocated` state. |
| 227 | |
| 228 | `Provision()` and `Cancel()` will ignore any sliver URNs which are not in the `geni_allocated` or `geni_updating` states. |
| 229 | |
| 230 | `Update()` can be called on slivers in any state. It will have no effect on slivers in the `geni_unallocated` state, and otherwise operates as described above. |
| 231 | |
| 232 | === RSpec Change === |
| 233 | |
| 234 | To easily accommodate submitting `Update()` requests, RSpecs will be changed: |
| 235 | * `sliver_id` will be allowed in request RSpecs |
| 236 | * Manifests will be modified to be re-usable as request RSpecs. In particular, the manifest schema will become a simple extension of the request schema, but this change is not required to support `Update()`. |