imip-agent

docs/wiki/Resources

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