paul@1032 | 1 | = Resources = |
paul@1032 | 2 | |
paul@1032 | 3 | In imip-agent, resources are a special kind of user that act upon requests |
paul@1032 | 4 | to schedule events and that perform such scheduling autonomously, meaning |
paul@1032 | 5 | that no human intervention is necessary when such resources receive messages |
paul@1032 | 6 | containing invitations. |
paul@1032 | 7 | |
paul@1032 | 8 | By default, the [[../AgentPrograms|agent program]] responsible for resources |
paul@1032 | 9 | merely attempts to fit a received event into the resource's schedule. However, |
paul@1032 | 10 | in some organisations and environments, it is likely to be the case that other |
paul@1032 | 11 | policies are needed to ensure that a resource is not misused, overused or made |
paul@1032 | 12 | unnecessarily unavailable. |
paul@1032 | 13 | |
paul@1032 | 14 | The [[../Preferences|preferences]] provide a way of controlling the behaviour |
paul@1032 | 15 | of resources, just as with any other kind of user, but certain preferences |
paul@1032 | 16 | are central to the configuration of resources. |
paul@1032 | 17 | |
paul@1032 | 18 | <<TableOfContents(2,4)>> |
paul@1032 | 19 | |
paul@1032 | 20 | == Scheduling Functions == |
paul@1032 | 21 | |
paul@1032 | 22 | The [[../Preferences#scheduling_function|scheduling_function]] setting |
paul@1032 | 23 | indicates the behaviour of a resource when a valid request to schedule an |
paul@1032 | 24 | event has been received. By default, a value equivalent to the following is |
paul@1032 | 25 | employed: |
paul@1032 | 26 | |
paul@1037 | 27 | {{{{#!table |
paul@1037 | 28 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1037 | 29 | == |
paul@1037 | 30 | <style="vertical-align: top;"> |
paul@1037 | 31 | |
paul@1032 | 32 | {{{ |
paul@1032 | 33 | schedule_in_freebusy |
paul@1032 | 34 | }}} |
paul@1032 | 35 | |
paul@1037 | 36 | || |
paul@1037 | 37 | |
paul@1037 | 38 | {{{#!graphviz |
paul@1037 | 39 | //format=svg |
paul@1037 | 40 | //transform=notugly |
paul@1037 | 41 | digraph scheduling_decisions { |
paul@1037 | 42 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1037 | 43 | edge [tooltip="Scheduling decisions"]; |
paul@1037 | 44 | |
paul@1037 | 45 | mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan]; |
paul@1037 | 46 | |
paul@1037 | 47 | subgraph { |
paul@1037 | 48 | rank=same; |
paul@1037 | 49 | schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 50 | freebusy [label="Free/busy",shape=folder]; |
paul@1037 | 51 | } |
paul@1037 | 52 | |
paul@1037 | 53 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 54 | |
paul@1039 | 55 | subgraph { |
paul@1039 | 56 | rank=same; |
paul@1039 | 57 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 58 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 59 | } |
paul@1037 | 60 | |
paul@1037 | 61 | mail -> schedule_in_freebusy -> schedule -> accept; |
paul@1037 | 62 | schedule_in_freebusy -> decline [style=dashed]; |
paul@1037 | 63 | freebusy -> schedule_in_freebusy; |
paul@1037 | 64 | } |
paul@1037 | 65 | }}} |
paul@1037 | 66 | |
paul@1037 | 67 | }}}} |
paul@1037 | 68 | |
paul@1032 | 69 | As described above, this merely attempts to schedule an event in the free |
paul@1032 | 70 | periods of the resource's schedule. However, no attempt is made to reject the |
paul@1032 | 71 | booking of the resource according to the identity of the organiser. |
paul@1032 | 72 | |
paul@1183 | 73 | === Concurrent Reservations === |
paul@1183 | 74 | |
paul@1183 | 75 | The `schedule_in_freebusy` function causes a resource to attempt to schedule |
paul@1183 | 76 | an event, and by default it rejects requests that involve periods for which |
paul@1183 | 77 | the resource is otherwise committed. However, a resource can be allowed to |
paul@1183 | 78 | attend (or commit to) multiple concurrent events. |
paul@1183 | 79 | |
paul@1183 | 80 | By indicating a value as an argument to the function, a kind of capacity or |
paul@1183 | 81 | commitment level can be assigned to a resource. For example: |
paul@1183 | 82 | |
paul@1183 | 83 | {{{ |
paul@1183 | 84 | schedule_in_freebusy 5 |
paul@1183 | 85 | }}} |
paul@1183 | 86 | |
paul@1183 | 87 | This example indicates that a resource can support five different events |
paul@1183 | 88 | occupying the same point in time. Applications of such concurrent reservations |
paul@1183 | 89 | include things like rooms or resources that can be shared and which have a |
paul@1183 | 90 | notion of a capacity that is not immediately exhausted as soon as one event |
paul@1183 | 91 | seeks to reserve such a room or resource. |
paul@1183 | 92 | |
paul@1032 | 93 | === Identity Controls === |
paul@1032 | 94 | |
paul@1032 | 95 | Although identity controls may be implemented in the e-mail system, |
paul@1032 | 96 | effectively preventing the messages from addresses other than those within |
paul@1032 | 97 | an organisation (for example) from being delivered to the resource, it is |
paul@1032 | 98 | possible to use scheduling functions to implement such controls instead. |
paul@1032 | 99 | |
paul@1032 | 100 | ==== Same Domain Membership ==== |
paul@1032 | 101 | |
paul@1032 | 102 | For instance, the following combines the default free/busy check with a |
paul@1032 | 103 | test that the organiser belongs to the same Internet mail domain (by using |
paul@1032 | 104 | the organiser's address): |
paul@1032 | 105 | |
paul@1037 | 106 | {{{{#!table |
paul@1037 | 107 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1037 | 108 | == |
paul@1037 | 109 | <style="vertical-align: top;"> |
paul@1037 | 110 | |
paul@1032 | 111 | {{{ |
paul@1032 | 112 | schedule_in_freebusy |
paul@1032 | 113 | same_domain_only |
paul@1032 | 114 | }}} |
paul@1032 | 115 | |
paul@1037 | 116 | || |
paul@1037 | 117 | |
paul@1037 | 118 | {{{#!graphviz |
paul@1037 | 119 | //format=svg |
paul@1037 | 120 | //transform=notugly |
paul@1037 | 121 | digraph scheduling_decisions { |
paul@1037 | 122 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1037 | 123 | edge [tooltip="Scheduling decisions"]; |
paul@1037 | 124 | |
paul@1037 | 125 | mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan]; |
paul@1037 | 126 | |
paul@1037 | 127 | subgraph { |
paul@1037 | 128 | rank=same; |
paul@1037 | 129 | schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 130 | freebusy [label="Free/busy",shape=folder]; |
paul@1037 | 131 | } |
paul@1037 | 132 | |
paul@1037 | 133 | same_domain_only [label="Organiser has resource domain?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 134 | |
paul@1037 | 135 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 136 | |
paul@1039 | 137 | subgraph { |
paul@1039 | 138 | rank=same; |
paul@1039 | 139 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 140 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 141 | } |
paul@1037 | 142 | |
paul@1037 | 143 | mail -> schedule_in_freebusy -> same_domain_only -> schedule -> accept; |
paul@1037 | 144 | schedule_in_freebusy -> decline [style=dashed]; |
paul@1037 | 145 | same_domain_only -> decline [style=dashed]; |
paul@1037 | 146 | freebusy -> schedule_in_freebusy; |
paul@1037 | 147 | } |
paul@1037 | 148 | }}} |
paul@1037 | 149 | |
paul@1037 | 150 | }}}} |
paul@1037 | 151 | |
paul@1032 | 152 | Note that if the first function is omitted, no check against the resource's |
paul@1032 | 153 | schedule will occur, so it is necessary to mention any such function in the |
paul@1032 | 154 | list. |
paul@1032 | 155 | |
paul@1032 | 156 | ==== Access Control Lists ==== |
paul@1032 | 157 | |
paul@1032 | 158 | A simple domain-related test may not be sufficient to control access to a |
paul@1032 | 159 | resource. Thus, another function is provided to exercise a finer degree of |
paul@1032 | 160 | control over event participants. For example: |
paul@1032 | 161 | |
paul@1037 | 162 | {{{{#!table |
paul@1037 | 163 | '''Scheduling Functions and Data''' || '''Decision Process''' |
paul@1037 | 164 | == |
paul@1037 | 165 | <style="vertical-align: top;"> |
paul@1037 | 166 | |
paul@1032 | 167 | {{{ |
paul@1032 | 168 | schedule_in_freebusy |
paul@1032 | 169 | access_control_list |
paul@1032 | 170 | }}} |
paul@1032 | 171 | |
paul@1037 | 172 | Access control list: |
paul@1037 | 173 | |
paul@1037 | 174 | {{{ |
paul@1037 | 175 | accept |
paul@1037 | 176 | }}} |
paul@1037 | 177 | |
paul@1037 | 178 | || |
paul@1037 | 179 | |
paul@1037 | 180 | {{{#!graphviz |
paul@1037 | 181 | //format=svg |
paul@1037 | 182 | //transform=notugly |
paul@1037 | 183 | digraph scheduling_decisions { |
paul@1037 | 184 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1037 | 185 | edge [tooltip="Scheduling decisions"]; |
paul@1037 | 186 | |
paul@1037 | 187 | mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan]; |
paul@1037 | 188 | |
paul@1037 | 189 | subgraph { |
paul@1037 | 190 | rank=same; |
paul@1037 | 191 | schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 192 | freebusy [label="Free/busy",shape=folder]; |
paul@1037 | 193 | } |
paul@1037 | 194 | |
paul@1037 | 195 | subgraph { |
paul@1037 | 196 | rank=same; |
paul@1037 | 197 | access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 198 | acl [label="acl setting",shape=folder]; |
paul@1037 | 199 | } |
paul@1037 | 200 | |
paul@1037 | 201 | accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 202 | end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 203 | |
paul@1037 | 204 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 205 | |
paul@1039 | 206 | subgraph { |
paul@1039 | 207 | rank=same; |
paul@1039 | 208 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 209 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 210 | } |
paul@1037 | 211 | |
paul@1037 | 212 | mail -> schedule_in_freebusy -> access_control_list -> accept_default -> end_acl -> schedule -> accept; |
paul@1037 | 213 | end_acl -> decline [style=dashed]; |
paul@1037 | 214 | schedule_in_freebusy -> decline [style=dashed]; |
paul@1037 | 215 | freebusy -> schedule_in_freebusy; |
paul@1037 | 216 | acl -> access_control_list; |
paul@1037 | 217 | } |
paul@1037 | 218 | }}} |
paul@1037 | 219 | |
paul@1037 | 220 | }}}} |
paul@1037 | 221 | |
paul@1037 | 222 | To accompany the scheduling functions, the [[../Preferences#acl|acl]] setting |
paul@1037 | 223 | in the resource's preferences must be set, or if a separate file is more |
paul@1037 | 224 | appropriate, its full path may be given as an argument to `access_control_list`: |
paul@1032 | 225 | |
paul@1032 | 226 | {{{ |
paul@1032 | 227 | schedule_in_freebusy |
paul@1032 | 228 | access_control_list /etc/imip-agent/resources.acl |
paul@1032 | 229 | }}} |
paul@1032 | 230 | |
paul@1032 | 231 | Within the file provided by the setting or separate file, a list of rules |
paul@1037 | 232 | must describe the handling procedure for an event. For example, the following |
paul@1037 | 233 | was given in the above example: |
paul@1032 | 234 | |
paul@1032 | 235 | {{{ |
paul@1032 | 236 | accept |
paul@1032 | 237 | }}} |
paul@1032 | 238 | |
paul@1032 | 239 | This will merely accept all invitations, anyway. However, it may be |
paul@1032 | 240 | appropriate to prevent certain users from using resources. For example: |
paul@1032 | 241 | |
paul@1037 | 242 | {{{{#!table |
paul@1037 | 243 | '''Scheduling Functions and Data''' || '''Decision Process''' |
paul@1037 | 244 | == |
paul@1037 | 245 | <style="vertical-align: top;"> |
paul@1037 | 246 | |
paul@1037 | 247 | {{{ |
paul@1037 | 248 | schedule_in_freebusy |
paul@1037 | 249 | access_control_list |
paul@1037 | 250 | }}} |
paul@1037 | 251 | |
paul@1037 | 252 | Access control list: |
paul@1037 | 253 | |
paul@1032 | 254 | {{{ |
paul@1032 | 255 | accept |
paul@1032 | 256 | decline attendee simon.skunk@example.com |
paul@1032 | 257 | }}} |
paul@1032 | 258 | |
paul@1037 | 259 | || |
paul@1037 | 260 | |
paul@1037 | 261 | {{{#!graphviz |
paul@1037 | 262 | //format=svg |
paul@1037 | 263 | //transform=notugly |
paul@1037 | 264 | digraph scheduling_decisions { |
paul@1037 | 265 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1037 | 266 | edge [tooltip="Scheduling decisions"]; |
paul@1037 | 267 | |
paul@1037 | 268 | mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan]; |
paul@1037 | 269 | |
paul@1037 | 270 | subgraph { |
paul@1037 | 271 | rank=same; |
paul@1037 | 272 | schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 273 | freebusy [label="Free/busy",shape=folder]; |
paul@1037 | 274 | } |
paul@1037 | 275 | |
paul@1037 | 276 | subgraph { |
paul@1037 | 277 | rank=same; |
paul@1037 | 278 | access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 279 | acl [label="acl setting",shape=folder]; |
paul@1037 | 280 | } |
paul@1037 | 281 | |
paul@1037 | 282 | accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 283 | decline_attendee [label="Is attendee simon.skunk@example.com?",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 284 | end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 285 | |
paul@1037 | 286 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 287 | |
paul@1039 | 288 | subgraph { |
paul@1039 | 289 | rank=same; |
paul@1039 | 290 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 291 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 292 | } |
paul@1037 | 293 | |
paul@1037 | 294 | mail -> schedule_in_freebusy -> access_control_list -> accept_default -> decline_attendee -> end_acl -> schedule -> accept; |
paul@1037 | 295 | end_acl -> decline [style=dashed]; |
paul@1037 | 296 | schedule_in_freebusy -> decline [style=dashed]; |
paul@1037 | 297 | freebusy -> schedule_in_freebusy; |
paul@1037 | 298 | acl -> access_control_list; |
paul@1037 | 299 | } |
paul@1037 | 300 | }}} |
paul@1037 | 301 | |
paul@1037 | 302 | }}}} |
paul@1037 | 303 | |
paul@1032 | 304 | This example indicates that by default, invitations will be accepted, but if |
paul@1032 | 305 | one of the attendees of an event is `simon.skunk@example.com`, the invitation |
paul@1032 | 306 | will be declined. However, it may be the case that this rule should be |
paul@1032 | 307 | overridden under certain circumstances. For example: |
paul@1032 | 308 | |
paul@1037 | 309 | {{{{#!table |
paul@1037 | 310 | '''Scheduling Functions and Data''' || '''Decision Process''' |
paul@1037 | 311 | == |
paul@1037 | 312 | <style="vertical-align: top;"> |
paul@1037 | 313 | |
paul@1037 | 314 | {{{ |
paul@1037 | 315 | schedule_in_freebusy |
paul@1037 | 316 | access_control_list |
paul@1037 | 317 | }}} |
paul@1037 | 318 | |
paul@1037 | 319 | Access control list: |
paul@1037 | 320 | |
paul@1032 | 321 | {{{ |
paul@1032 | 322 | accept |
paul@1032 | 323 | decline attendee simon.skunk@example.com |
paul@1032 | 324 | accept organiser paul.boddie@example.com |
paul@1032 | 325 | }}} |
paul@1032 | 326 | |
paul@1037 | 327 | || |
paul@1037 | 328 | |
paul@1037 | 329 | {{{#!graphviz |
paul@1037 | 330 | //format=svg |
paul@1037 | 331 | //transform=notugly |
paul@1037 | 332 | digraph scheduling_decisions { |
paul@1037 | 333 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1037 | 334 | edge [tooltip="Scheduling decisions"]; |
paul@1037 | 335 | |
paul@1037 | 336 | mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan]; |
paul@1037 | 337 | |
paul@1037 | 338 | subgraph { |
paul@1037 | 339 | rank=same; |
paul@1037 | 340 | schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 341 | freebusy [label="Free/busy",shape=folder]; |
paul@1037 | 342 | } |
paul@1037 | 343 | |
paul@1037 | 344 | subgraph { |
paul@1037 | 345 | rank=same; |
paul@1037 | 346 | access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1037 | 347 | acl [label="acl setting",shape=folder]; |
paul@1037 | 348 | } |
paul@1037 | 349 | |
paul@1037 | 350 | accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 351 | decline_attendee [label="Is attendee simon.skunk@example.com?",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 352 | accept_organiser [label="Is organiser paul.boddie@example.com?",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 353 | end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1037 | 354 | |
paul@1037 | 355 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 356 | |
paul@1039 | 357 | subgraph { |
paul@1039 | 358 | rank=same; |
paul@1039 | 359 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 360 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 361 | } |
paul@1037 | 362 | |
paul@1037 | 363 | mail -> schedule_in_freebusy -> access_control_list -> accept_default -> decline_attendee -> accept_organiser -> end_acl -> schedule -> accept; |
paul@1037 | 364 | end_acl -> decline [style=dashed]; |
paul@1037 | 365 | schedule_in_freebusy -> decline [style=dashed]; |
paul@1037 | 366 | freebusy -> schedule_in_freebusy; |
paul@1037 | 367 | acl -> access_control_list; |
paul@1037 | 368 | } |
paul@1037 | 369 | }}} |
paul@1037 | 370 | |
paul@1037 | 371 | }}}} |
paul@1037 | 372 | |
paul@1032 | 373 | Here, the stated organiser may still arrange a booking of the resource where |
paul@1032 | 374 | the previously-mentioned attendee is involved. |
paul@1039 | 375 | |
paul@1039 | 376 | === Quota Controls === |
paul@1039 | 377 | |
paul@1039 | 378 | In contrast to each user's stored information which consolidates information |
paul@1227 | 379 | related to that user's own schedule in a section of the data store, the quota |
paul@1227 | 380 | system consolidates information related to the schedules of one or more |
paul@1227 | 381 | resources in a repository known as the journal, thus enabling observations to |
paul@1227 | 382 | be made about their collective usage. |
paul@1039 | 383 | |
paul@1039 | 384 | First, consider a resource such as a car where an organiser of an event may be |
paul@1039 | 385 | booking the car for travel purposes. A quota prevents the organiser from |
paul@1039 | 386 | booking the resource too much and denying other users access to it. |
paul@1039 | 387 | |
paul@1227 | 388 | {{{#!graphviz |
paul@1227 | 389 | //format=svg |
paul@1227 | 390 | //transform=notugly |
paul@1227 | 391 | digraph quota_users { |
paul@1227 | 392 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota example"]; |
paul@1227 | 393 | edge [tooltip="Quota example"]; |
paul@1227 | 394 | |
paul@1227 | 395 | subgraph { |
paul@1227 | 396 | rank=same; |
paul@1227 | 397 | reservation1 [label="Reservation from oliver.otter@example.com\nMonday at 10am",shape=folder,style=filled,fillcolor=cyan]; |
paul@1227 | 398 | reservation2 [label="Reservation from oliver.otter@example.com\nTuesday at 10am",shape=folder,style=filled,fillcolor=cyan]; |
paul@1227 | 399 | reservation3 [label="Reservation from oliver.otter@example.com\nWednesday at 10am",shape=folder,style=filled,fillcolor=cyan]; |
paul@1227 | 400 | } |
paul@1227 | 401 | |
paul@1227 | 402 | subgraph { |
paul@1227 | 403 | rank=same; |
paul@1227 | 404 | test1 [label="Within quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1227 | 405 | test2 [label="Within quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1227 | 406 | test3 [label="Within quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1227 | 407 | } |
paul@1227 | 408 | |
paul@1227 | 409 | subgraph { |
paul@1227 | 410 | rank=max; |
paul@1227 | 411 | quota [label="Quota for resource-car-pontiac@example.com",shape=folder]; |
paul@1227 | 412 | } |
paul@1227 | 413 | |
paul@1227 | 414 | reservation1 -> test1; |
paul@1227 | 415 | reservation2 -> test2; |
paul@1227 | 416 | reservation3 -> test3; |
paul@1227 | 417 | |
paul@1227 | 418 | quota -> test1; |
paul@1227 | 419 | quota -> test2; |
paul@1227 | 420 | quota -> test3; |
paul@1227 | 421 | } |
paul@1227 | 422 | }}} |
paul@1227 | 423 | |
paul@1039 | 424 | Now consider a number of separate car resources. An organiser might attempt to |
paul@1039 | 425 | get around any individual resource quota by booking a number of different cars. |
paul@1227 | 426 | Since each car is only aware of its own usage, it would be unaware of the |
paul@1227 | 427 | undesirable cumulative usage of all cars by the organiser. |
paul@1227 | 428 | |
paul@1167 | 429 | By grouping the resources together, the organiser will exhaust any quota set on |
paul@1167 | 430 | the group of resources as they try and make reservations for the different |
paul@1227 | 431 | members of the quota group. Some additional measures are introduced as follows: |
paul@1227 | 432 | |
paul@1227 | 433 | {{{#!graphviz |
paul@1227 | 434 | //format=svg |
paul@1227 | 435 | //transform=notugly |
paul@1227 | 436 | digraph quota_users { |
paul@1227 | 437 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota example"]; |
paul@1227 | 438 | edge [tooltip="Quota example"]; |
paul@1227 | 439 | |
paul@1227 | 440 | subgraph { |
paul@1227 | 441 | rank=same; |
paul@1227 | 442 | reservation1 [label="Reservation from oliver.otter@example.com\nMonday at 10am",shape=folder,style=filled,fillcolor=cyan]; |
paul@1227 | 443 | reservation2 [label="Reservation from oliver.otter@example.com\nTuesday at 10am",shape=folder,style=filled,fillcolor=cyan]; |
paul@1227 | 444 | reservation3 [label="Reservation from oliver.otter@example.com\nWednesday at 10am",shape=folder,style=filled,fillcolor=cyan]; |
paul@1227 | 445 | } |
paul@1227 | 446 | |
paul@1227 | 447 | subgraph { |
paul@1227 | 448 | rank=same; |
paul@1227 | 449 | car1 [label="resource-car-pontiac@example.com",shape=folder]; |
paul@1227 | 450 | car2 [label="resource-car-cadillac@example.com",shape=folder]; |
paul@1227 | 451 | car3 [label="resource-car-lexus@example.com",shape=folder]; |
paul@1227 | 452 | } |
paul@1227 | 453 | |
paul@1227 | 454 | test [label="Within common quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1227 | 455 | |
paul@1227 | 456 | subgraph { |
paul@1227 | 457 | rank=max; |
paul@1227 | 458 | journal [label="Quota for cars",shape=folder]; |
paul@1227 | 459 | } |
paul@1227 | 460 | |
paul@1227 | 461 | reservation1 -> car1 -> test; |
paul@1227 | 462 | reservation2 -> car2 -> test; |
paul@1227 | 463 | reservation3 -> car3 -> test; |
paul@1227 | 464 | |
paul@1227 | 465 | journal -> test; |
paul@1227 | 466 | } |
paul@1227 | 467 | }}} |
paul@1039 | 468 | |
paul@1039 | 469 | ==== Initialising Quotas ==== |
paul@1039 | 470 | |
paul@1165 | 471 | Within the journal storage area, a quota may be initialised with limits |
paul@1165 | 472 | indicating the amount of time that can be occupied by the cumulative total of |
paul@1165 | 473 | all events scheduled by an individual user or a group of which they are a |
paul@1165 | 474 | member. |
paul@1165 | 475 | |
paul@1165 | 476 | Such limits may be set directly using the `limits` file in a quota directory |
paul@1165 | 477 | (described in the [[../FilesystemUsage|filesystem guide]]) or in the |
paul@1165 | 478 | `quota_limits` table (described in the [[../DatabaseStore|database guide]]), |
paul@1165 | 479 | but a tool is also provided to set such limits. For example: |
paul@1039 | 480 | |
paul@1039 | 481 | {{{ |
paul@1178 | 482 | cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com' |
paul@1178 | 483 | mailto:vincent.vole@example.com PT10H |
paul@1178 | 484 | EOF |
paul@1039 | 485 | }}} |
paul@1039 | 486 | |
paul@1178 | 487 | {{{#!wiki tip |
paul@1178 | 488 | In the above example, shell syntax is used to indicate a |
paul@1178 | 489 | [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04|here document]] |
paul@1178 | 490 | providing a kind of "inline" file that is terminated by the final `EOF`. |
paul@1178 | 491 | The contents of this file are piped to the tool with a single argument given |
paul@1178 | 492 | indicating the quota involved. |
paul@1178 | 493 | |
paul@1178 | 494 | You could also just invoke the tool and then enter the limit descriptions, |
paul@1178 | 495 | ending the input with Ctrl-D or equivalent end-of-file keystroke, or save |
paul@1178 | 496 | the descriptions in a file and then use input redirection with the filename. |
paul@1178 | 497 | }}} |
paul@1178 | 498 | |
paul@1178 | 499 | The above example indicates that the given user may only reserve 10 hours |
paul@1178 | 500 | of events or less time within the given quota (corresponding to a specific |
paul@1178 | 501 | resource in the above example). Attempts to schedule more time will be |
paul@1178 | 502 | declined. |
paul@1039 | 503 | |
paul@1039 | 504 | To impose a general quota, the special `*` identity can be used: |
paul@1039 | 505 | |
paul@1039 | 506 | {{{ |
paul@1178 | 507 | cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com' |
paul@1178 | 508 | * PT10H |
paul@1178 | 509 | EOF |
paul@1039 | 510 | }}} |
paul@1039 | 511 | |
paul@1180 | 512 | Note that this general quota applies to each individual identity and not |
paul@1180 | 513 | collectively to all unspecified identities. To impose such a collective |
paul@1180 | 514 | quota, a group may be defined for this purpose as described below. |
paul@1180 | 515 | |
paul@1039 | 516 | When a user identity is not listed and no general quota is defined, that |
paul@1039 | 517 | particular user will be unable to reserve the resource unless defined as a |
paul@1039 | 518 | member of a group listed in the `limits` file, as described below. |
paul@1039 | 519 | |
paul@1190 | 520 | It may be useful to define unlimited quotas for certain identities or |
paul@1190 | 521 | groups in order to effectively exclude them from limits. For example: |
paul@1190 | 522 | |
paul@1190 | 523 | {{{ |
paul@1190 | 524 | cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com' |
paul@1190 | 525 | mailto:vincent.vole@example.com * |
paul@1190 | 526 | EOF |
paul@1190 | 527 | }}} |
paul@1190 | 528 | |
paul@1039 | 529 | ==== Sharing Quotas Across Users ==== |
paul@1039 | 530 | |
paul@1039 | 531 | When the use of resources is to be shared between users in such a way that |
paul@1180 | 532 | groups of users will be sharing a single quota, a `groups` file in a quota |
paul@1180 | 533 | directory (or records in the `quota_groups` table) must be defined, mapping |
paul@1180 | 534 | each user identity to the group to which they will belong. |
paul@1180 | 535 | |
paul@1180 | 536 | A tool is provided to define groups and is used as follows: |
paul@1180 | 537 | |
paul@1180 | 538 | {{{ |
paul@1180 | 539 | cat <<EOF | tools/set_quota_groups.py 'mailto:resource-car-cadillac@example.com' |
paul@1180 | 540 | mailto:vincent.vole@example.com developers |
paul@1180 | 541 | * others |
paul@1180 | 542 | EOF |
paul@1180 | 543 | }}} |
paul@1180 | 544 | |
paul@1180 | 545 | Here, otherwise unrecognised organisers are mapped to the `others` group. |
paul@1180 | 546 | Thus, all scheduling performed by such organisers will be done in a common |
paul@1180 | 547 | journal with this label. |
paul@1180 | 548 | |
paul@1180 | 549 | The process of determining where an organiser's usage of resources is |
paul@1180 | 550 | recorded is illustrated by the following example: |
paul@1039 | 551 | |
paul@1167 | 552 | {{{{#!table |
paul@1167 | 553 | '''Scheduling Data''' || '''Decision Process''' |
paul@1167 | 554 | == |
paul@1167 | 555 | <style="vertical-align: top;"> |
paul@1167 | 556 | The groups are defined as follows in the `groups` file: |
paul@1167 | 557 | |
paul@1039 | 558 | {{{ |
paul@1039 | 559 | mailto:vincent.vole@example.com developers |
paul@1039 | 560 | mailto:harvey.horse@example.com developers |
paul@1039 | 561 | mailto:paul.boddie@example.com developers |
paul@1039 | 562 | mailto:simon.skunk@example.com testers |
paul@1039 | 563 | }}} |
paul@1039 | 564 | |
paul@1039 | 565 | The group identity can then be employed in the `limits` file: |
paul@1039 | 566 | |
paul@1039 | 567 | {{{ |
paul@1039 | 568 | developers PT10H |
paul@1039 | 569 | testers PT20H |
paul@1039 | 570 | }}} |
paul@1039 | 571 | |
paul@1167 | 572 | || |
paul@1167 | 573 | |
paul@1167 | 574 | {{{#!graphviz |
paul@1167 | 575 | //format=svg |
paul@1167 | 576 | //transform=notugly |
paul@1167 | 577 | digraph quota_users { |
paul@1167 | 578 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota users"]; |
paul@1167 | 579 | edge [tooltip="Quota users"]; |
paul@1167 | 580 | |
paul@1167 | 581 | subgraph { |
paul@1167 | 582 | rank=same; |
paul@1167 | 583 | user1 [label="User is vincent.vole@example.com"]; |
paul@1167 | 584 | user2 [label="User is oliver.otter@example.com"]; |
paul@1167 | 585 | } |
paul@1167 | 586 | |
paul@1167 | 587 | havegroup [label="Have group for quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1167 | 588 | haveuser [label="Have group for user in quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1167 | 589 | |
paul@1167 | 590 | group1 [label="User is vincent.vole@example.com\nGroup is developers"]; |
paul@1167 | 591 | group2 [label="User is oliver.otter@example.com"]; |
paul@1167 | 592 | |
paul@1167 | 593 | checkuser1 [label="Have limit for group?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1167 | 594 | checkgeneral1 [label="Have general limit?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1167 | 595 | |
paul@1167 | 596 | checkuser2 [label="Have limit for user?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1167 | 597 | checkgeneral2 [label="Have general limit?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1167 | 598 | |
paul@1167 | 599 | accept [label="Quota",shape=folder,style=filled,fillcolor=cyan]; |
paul@1167 | 600 | decline [label="No quota",shape=folder,style=filled,fillcolor=cyan]; |
paul@1167 | 601 | |
paul@1167 | 602 | user1 -> havegroup -> haveuser -> group1 -> checkuser1 -> checkgeneral1 -> accept; |
paul@1167 | 603 | user2 -> havegroup -> haveuser -> group2 -> checkuser2 -> checkgeneral2 -> decline [style=dashed]; |
paul@1167 | 604 | } |
paul@1167 | 605 | }}} |
paul@1167 | 606 | }}}} |
paul@1167 | 607 | |
paul@1167 | 608 | Where individuals are not assigned to groups, any individual limit will apply |
paul@1167 | 609 | to them; otherwise, the general quota applies. Where individuals are assigned |
paul@1167 | 610 | to groups, any group limit will apply; otherwise, the general quota applies. |
paul@1039 | 611 | |
paul@1039 | 612 | ==== Individual Resource Quotas ==== |
paul@1039 | 613 | |
paul@1039 | 614 | The trivial case of applying quotas is to give a resource its own quota. This |
paul@1039 | 615 | is achieved by not specifying any arguments to the `check_quota` scheduling |
paul@1058 | 616 | function or to the `add_to_quota` and `remove_from_quota` functions. |
paul@1058 | 617 | |
paul@1058 | 618 | {{{{#!table |
paul@1058 | 619 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1058 | 620 | == |
paul@1058 | 621 | <style="vertical-align: top;"> |
paul@1058 | 622 | |
paul@1058 | 623 | {{{ |
paul@1058 | 624 | check_quota |
paul@1058 | 625 | }}} |
paul@1058 | 626 | |
paul@1058 | 627 | || |
paul@1058 | 628 | |
paul@1058 | 629 | {{{#!graphviz |
paul@1058 | 630 | //format=svg |
paul@1058 | 631 | //transform=notugly |
paul@1058 | 632 | digraph scheduling_decisions { |
paul@1058 | 633 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1058 | 634 | edge [tooltip="Scheduling decisions"]; |
paul@1058 | 635 | |
paul@1058 | 636 | subgraph { |
paul@1058 | 637 | rank=same; |
paul@1058 | 638 | mail [label="Incoming mail\nfrom vincent.vole@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1058 | 639 | cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan]; |
paul@1058 | 640 | } |
paul@1058 | 641 | |
paul@1058 | 642 | subgraph { |
paul@1058 | 643 | rank=same; |
paul@1058 | 644 | check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1058 | 645 | quota [label="Quota for resource",shape=folder]; |
paul@1058 | 646 | quota_for_vole [label="...applying to\nvincent.vole@example.com",shape=folder]; |
paul@1058 | 647 | } |
paul@1058 | 648 | |
paul@1058 | 649 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1058 | 650 | |
paul@1058 | 651 | subgraph { |
paul@1058 | 652 | rank=same; |
paul@1058 | 653 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1058 | 654 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1058 | 655 | } |
paul@1058 | 656 | |
paul@1058 | 657 | add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1058 | 658 | remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1058 | 659 | |
paul@1058 | 660 | mail -> check_quota -> schedule -> accept; |
paul@1058 | 661 | check_quota -> decline [style=dashed]; |
paul@1058 | 662 | schedule -> add_to_quota -> quota; |
paul@1058 | 663 | quota -> quota_for_vole -> check_quota; |
paul@1058 | 664 | |
paul@1058 | 665 | cancel -> remove_from_quota -> quota; |
paul@1058 | 666 | } |
paul@1058 | 667 | }}} |
paul@1058 | 668 | |
paul@1058 | 669 | }}}} |
paul@1039 | 670 | |
paul@1039 | 671 | ==== Common Resource Quotas ==== |
paul@1039 | 672 | |
paul@1039 | 673 | By indicating an argument to the different functions, a common quota can be |
paul@1039 | 674 | employed. In the following example, both resources would employ the given |
paul@1039 | 675 | function invocations to pool their knowledge about their schedules. |
paul@1039 | 676 | |
paul@1039 | 677 | {{{{#!table |
paul@1058 | 678 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1039 | 679 | == |
paul@1039 | 680 | <style="vertical-align: top;"> |
paul@1039 | 681 | |
paul@1039 | 682 | {{{ |
paul@1039 | 683 | check_quota cars |
paul@1039 | 684 | }}} |
paul@1039 | 685 | |
paul@1039 | 686 | || |
paul@1039 | 687 | |
paul@1039 | 688 | {{{#!graphviz |
paul@1039 | 689 | //format=svg |
paul@1039 | 690 | //transform=notugly |
paul@1039 | 691 | digraph scheduling_decisions { |
paul@1039 | 692 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1039 | 693 | edge [tooltip="Scheduling decisions"]; |
paul@1039 | 694 | |
paul@1039 | 695 | subgraph { |
paul@1039 | 696 | rank=same; |
paul@1039 | 697 | mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 698 | mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 699 | cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 700 | } |
paul@1039 | 701 | |
paul@1039 | 702 | subgraph { |
paul@1039 | 703 | rank=same; |
paul@1039 | 704 | check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 705 | quota_cars [label="Quota for cars",shape=folder]; |
paul@1039 | 706 | quota_cars_vole [label="...applying to\nvincent.vole@example.com",shape=folder]; |
paul@1039 | 707 | } |
paul@1039 | 708 | |
paul@1039 | 709 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 710 | |
paul@1039 | 711 | subgraph { |
paul@1039 | 712 | rank=same; |
paul@1039 | 713 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 714 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 715 | } |
paul@1039 | 716 | |
paul@1039 | 717 | add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1039 | 718 | remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1039 | 719 | |
paul@1039 | 720 | mail_cadillac -> check_quota; |
paul@1039 | 721 | mail_pontiac -> check_quota -> schedule -> accept; |
paul@1039 | 722 | check_quota -> decline [style=dashed]; |
paul@1039 | 723 | schedule -> add_to_quota -> quota_cars; |
paul@1039 | 724 | quota_cars -> quota_cars_vole -> check_quota; |
paul@1039 | 725 | |
paul@1039 | 726 | cancel -> remove_from_quota -> quota_cars; |
paul@1039 | 727 | } |
paul@1039 | 728 | }}} |
paul@1039 | 729 | |
paul@1039 | 730 | }}}} |
paul@1039 | 731 | |
paul@1039 | 732 | ==== Collective Scheduling ==== |
paul@1039 | 733 | |
paul@1039 | 734 | Consider two separate resources: both may be reserved at the same time by the |
paul@1039 | 735 | same organiser; neither resource would normally decline the reservation on the |
paul@1039 | 736 | basis of schedule availability, should the period concerned be free. However, |
paul@1039 | 737 | it may be undesirable for one organiser to occupy both resources at the same |
paul@1039 | 738 | time. |
paul@1039 | 739 | |
paul@1039 | 740 | Consequently, a mechanism is required to pool the resource schedules in such a |
paul@1039 | 741 | way that any reservation performed for one resource at a given point in time |
paul@1039 | 742 | prohibits another reservation performed for a related resource at the same |
paul@1039 | 743 | point in time by the same user. |
paul@1039 | 744 | |
paul@1039 | 745 | The free/busy records held for a given quota group permit such collective |
paul@1039 | 746 | scheduling decisions and are employed as follows: |
paul@1039 | 747 | |
paul@1039 | 748 | {{{{#!table |
paul@1058 | 749 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1039 | 750 | == |
paul@1039 | 751 | <style="vertical-align: top;"> |
paul@1039 | 752 | |
paul@1039 | 753 | {{{ |
paul@1164 | 754 | schedule_across_quota cars |
paul@1039 | 755 | }}} |
paul@1039 | 756 | |
paul@1039 | 757 | || |
paul@1039 | 758 | |
paul@1039 | 759 | {{{#!graphviz |
paul@1039 | 760 | //format=svg |
paul@1039 | 761 | //transform=notugly |
paul@1039 | 762 | digraph scheduling_decisions { |
paul@1039 | 763 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1039 | 764 | edge [tooltip="Scheduling decisions"]; |
paul@1039 | 765 | |
paul@1039 | 766 | subgraph { |
paul@1039 | 767 | rank=same; |
paul@1039 | 768 | mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 769 | mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 770 | cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 771 | } |
paul@1039 | 772 | |
paul@1039 | 773 | subgraph { |
paul@1039 | 774 | rank=same; |
paul@1039 | 775 | schedule_across_quota [label="Can be scheduled within the quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 776 | quota_cars [label="Quota for cars",shape=folder]; |
paul@1039 | 777 | freebusy_cars_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder]; |
paul@1039 | 778 | } |
paul@1039 | 779 | |
paul@1039 | 780 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1039 | 781 | |
paul@1039 | 782 | subgraph { |
paul@1039 | 783 | rank=same; |
paul@1039 | 784 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 785 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1039 | 786 | } |
paul@1039 | 787 | |
paul@1039 | 788 | add_to_quota_freebusy [label="Add to quota free/busy",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1039 | 789 | remove_from_quota_freebusy [label="Remove from quota free/busy",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1039 | 790 | |
paul@1039 | 791 | mail_cadillac -> schedule_across_quota; |
paul@1039 | 792 | mail_pontiac -> schedule_across_quota -> schedule -> accept; |
paul@1039 | 793 | schedule_across_quota -> decline [style=dashed]; |
paul@1039 | 794 | schedule -> add_to_quota_freebusy -> quota_cars -> freebusy_cars_vole; |
paul@1039 | 795 | freebusy_cars_vole -> schedule_across_quota; |
paul@1039 | 796 | |
paul@1190 | 797 | cancel -> remove_from_quota_freebusy -> quota_cars; |
paul@1039 | 798 | } |
paul@1039 | 799 | }}} |
paul@1039 | 800 | |
paul@1039 | 801 | }}}} |
paul@1180 | 802 | |
paul@1180 | 803 | ==== Delegating Attendance ==== |
paul@1180 | 804 | |
paul@1180 | 805 | A number of resources may be regarded as interchangeable and can therefore |
paul@1180 | 806 | stand in for each other when they are unavailable. The iCalendar specification |
paul@1180 | 807 | supports the notion of delegation: the recipient of an event invitation may |
paul@1180 | 808 | delegate their attendance to other calendar user, informing that user and the |
paul@1180 | 809 | event organiser of this decision. |
paul@1180 | 810 | |
paul@1180 | 811 | To define such delegation relationships, a quota is first selected as the |
paul@1180 | 812 | repository of common scheduling information for a group of resources. Then, |
paul@1180 | 813 | the members of the group are defined as delegates. This can be done using a |
paul@1180 | 814 | tool provided for this purpose. For example: |
paul@1180 | 815 | |
paul@1180 | 816 | {{{ |
paul@1180 | 817 | cat <<EOF | tools/set_delegates.py 'cars' |
paul@1180 | 818 | mailto:resource-car-cadillac@example.com |
paul@1180 | 819 | mailto:resource-car-pontiac@example.com |
paul@1180 | 820 | EOF |
paul@1180 | 821 | }}} |
paul@1180 | 822 | |
paul@1180 | 823 | Here, two resources are defined that will delegate to each other if they |
paul@1180 | 824 | cannot attend an event according to their own schedule. |
paul@1180 | 825 | |
paul@1180 | 826 | {{{{#!table |
paul@1180 | 827 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1180 | 828 | == |
paul@1180 | 829 | <style="vertical-align: top;"> |
paul@1180 | 830 | |
paul@1180 | 831 | {{{ |
paul@1180 | 832 | schedule_for_delegate cars |
paul@1180 | 833 | }}} |
paul@1180 | 834 | |
paul@1180 | 835 | || |
paul@1180 | 836 | |
paul@1180 | 837 | {{{#!graphviz |
paul@1180 | 838 | //format=svg |
paul@1180 | 839 | //transform=notugly |
paul@1180 | 840 | digraph scheduling_decisions { |
paul@1180 | 841 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1180 | 842 | edge [tooltip="Scheduling decisions"]; |
paul@1180 | 843 | |
paul@1180 | 844 | subgraph { |
paul@1180 | 845 | rank=same; |
paul@1180 | 846 | mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1180 | 847 | mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1180 | 848 | cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan]; |
paul@1180 | 849 | } |
paul@1180 | 850 | |
paul@1180 | 851 | subgraph { |
paul@1180 | 852 | rank=same; |
paul@1180 | 853 | schedule_for_delegate [label="Can be scheduled or delegated?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1180 | 854 | quota_cars [label="Quota for cars",shape=folder]; |
paul@1180 | 855 | freebusy_cars_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder]; |
paul@1180 | 856 | } |
paul@1180 | 857 | |
paul@1180 | 858 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1180 | 859 | |
paul@1180 | 860 | subgraph { |
paul@1180 | 861 | rank=same; |
paul@1180 | 862 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1180 | 863 | delegate [label="Delegate",shape=folder,style=filled,fillcolor=cyan]; |
paul@1180 | 864 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1180 | 865 | } |
paul@1180 | 866 | |
paul@1180 | 867 | add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1180 | 868 | remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1180 | 869 | |
paul@1180 | 870 | mail_cadillac -> schedule_for_delegate; |
paul@1180 | 871 | mail_pontiac -> schedule_for_delegate -> schedule -> accept; |
paul@1180 | 872 | schedule_for_delegate -> delegate [style=dashed]; |
paul@1180 | 873 | schedule_for_delegate -> decline [style=dashed]; |
paul@1180 | 874 | schedule -> add_to_quota -> quota_cars -> freebusy_cars_vole; |
paul@1180 | 875 | freebusy_cars_vole -> schedule_for_delegate; |
paul@1180 | 876 | |
paul@1190 | 877 | cancel -> remove_from_quota -> quota_cars; |
paul@1180 | 878 | } |
paul@1180 | 879 | }}} |
paul@1180 | 880 | |
paul@1180 | 881 | }}}} |
paul@1180 | 882 | |
paul@1180 | 883 | Note that it is generally more useful to have delegation decisions made on the |
paul@1180 | 884 | basis of many resources, which is what the journal entries for each quota |
paul@1190 | 885 | provides, but also considering organisers attempting to reserve those resources |
paul@1190 | 886 | as a single group whose reservations are consolidated and assessed collectively. |
paul@1190 | 887 | Thus, a single group of users would be defined: |
paul@1190 | 888 | |
paul@1190 | 889 | {{{ |
paul@1190 | 890 | cat <<EOF | tools/set_quota_groups.py 'cars' |
paul@1190 | 891 | * all |
paul@1190 | 892 | EOF |
paul@1190 | 893 | }}} |
paul@1190 | 894 | |
paul@1180 | 895 | Although the journal for each quota may be divided up to administer quotas for |
paul@1180 | 896 | multiple groups of organisers, for the purposes of delegation - deciding |
paul@1180 | 897 | whether a resource is generally available, and deciding which other resource |
paul@1180 | 898 | would be available instead - a single group of all organisers is more desirable. |
paul@1190 | 899 | |
paul@1190 | 900 | If limits need to be imposed alongside delegation, separate quotas may be used |
paul@1190 | 901 | to achieve this. |
paul@1190 | 902 | |
paul@1190 | 903 | {{{{#!table |
paul@1190 | 904 | '''Scheduling Functions''' || '''Decision Process''' |
paul@1190 | 905 | == |
paul@1190 | 906 | <style="vertical-align: top;"> |
paul@1190 | 907 | |
paul@1190 | 908 | {{{ |
paul@1190 | 909 | check_quota cars |
paul@1190 | 910 | schedule_for_delegate car_delegation |
paul@1190 | 911 | }}} |
paul@1190 | 912 | |
paul@1190 | 913 | || |
paul@1190 | 914 | |
paul@1190 | 915 | {{{#!graphviz |
paul@1190 | 916 | //format=svg |
paul@1190 | 917 | //transform=notugly |
paul@1190 | 918 | digraph scheduling_decisions { |
paul@1190 | 919 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"]; |
paul@1190 | 920 | edge [tooltip="Scheduling decisions"]; |
paul@1190 | 921 | |
paul@1190 | 922 | subgraph { |
paul@1190 | 923 | rank=same; |
paul@1190 | 924 | mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1190 | 925 | mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan]; |
paul@1190 | 926 | cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan]; |
paul@1190 | 927 | } |
paul@1190 | 928 | |
paul@1190 | 929 | subgraph { |
paul@1190 | 930 | rank=same; |
paul@1190 | 931 | check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1190 | 932 | quota_cars [label="Quota for cars",shape=folder]; |
paul@1190 | 933 | quota_cars_vole [label="...applying to\nvincent.vole@example.com",shape=folder]; |
paul@1190 | 934 | } |
paul@1190 | 935 | |
paul@1190 | 936 | subgraph { |
paul@1190 | 937 | rank=same; |
paul@1190 | 938 | schedule_for_delegate [label="Can be scheduled or delegated?",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1190 | 939 | quota_cars_delegation [label="Quota for cars_delegation",shape=folder]; |
paul@1190 | 940 | freebusy_cars_delegation_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder]; |
paul@1190 | 941 | } |
paul@1190 | 942 | |
paul@1190 | 943 | schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold]; |
paul@1190 | 944 | |
paul@1190 | 945 | subgraph { |
paul@1190 | 946 | rank=same; |
paul@1190 | 947 | accept [label="Accept",shape=folder,style=filled,fillcolor=cyan]; |
paul@1190 | 948 | delegate [label="Delegate",shape=folder,style=filled,fillcolor=cyan]; |
paul@1190 | 949 | decline [label="Decline",shape=folder,style=filled,fillcolor=cyan]; |
paul@1190 | 950 | } |
paul@1190 | 951 | |
paul@1190 | 952 | add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1190 | 953 | remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange]; |
paul@1190 | 954 | |
paul@1190 | 955 | mail_cadillac -> check_quota; |
paul@1190 | 956 | mail_pontiac -> check_quota; |
paul@1190 | 957 | check_quota -> schedule_for_delegate; |
paul@1190 | 958 | check_quota -> decline [style=dashed]; |
paul@1190 | 959 | schedule_for_delegate -> schedule -> accept; |
paul@1190 | 960 | schedule_for_delegate -> delegate [style=dashed]; |
paul@1190 | 961 | schedule_for_delegate -> decline [style=dashed]; |
paul@1190 | 962 | schedule -> add_to_quota; |
paul@1190 | 963 | add_to_quota -> quota_cars -> quota_cars_vole; |
paul@1190 | 964 | add_to_quota -> quota_cars_delegation -> freebusy_cars_delegation_vole; |
paul@1190 | 965 | quota_cars_vole -> check_quota; |
paul@1190 | 966 | freebusy_cars_delegation_vole -> schedule_for_delegate; |
paul@1190 | 967 | |
paul@1190 | 968 | cancel -> remove_from_quota; |
paul@1190 | 969 | remove_from_quota -> quota_cars; |
paul@1190 | 970 | remove_from_quota -> quota_cars_delegation; |
paul@1190 | 971 | } |
paul@1190 | 972 | }}} |
paul@1190 | 973 | |
paul@1190 | 974 | }}}} |