<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[14103] CalendarServer/trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/14103">14103</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2014-10-24 14:12:40 -0700 (Fri, 24 Oct 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Directory proxy/delegate memcache based caching.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcalendarservertoolsgatewaypy">CalendarServer/trunk/calendarserver/tools/gateway.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolsprincipalspy">CalendarServer/trunk/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServertrunkconfcaldavdtestpodAplist">CalendarServer/trunk/conf/caldavd-test-podA.plist</a></li>
<li><a href="#CalendarServertrunkconfcaldavdtestpodBplist">CalendarServer/trunk/conf/caldavd-test-podB.plist</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectorycalendaruserproxypy">CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectorycalendaruserproxyloaderpy">CalendarServer/trunk/twistedcaldav/directory/calendaruserproxyloader.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectoryprincipalpy">CalendarServer/trunk/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectorytesttest_principalpy">CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavstdconfigpy">CalendarServer/trunk/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavtesttest_upgradepy">CalendarServer/trunk/twistedcaldav/test/test_upgrade.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavupgradepy">CalendarServer/trunk/twistedcaldav/upgrade.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastorepoddingdirectorypy">CalendarServer/trunk/txdav/common/datastore/podding/directory.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoresqlpy">CalendarServer/trunk/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavdpsserverpy">CalendarServer/trunk/txdav/dps/server.py</a></li>
<li><a href="#CalendarServertrunktxdavwhodelegatespy">CalendarServer/trunk/txdav/who/delegates.py</a></li>
<li><a href="#CalendarServertrunktxdavwhotesttest_delegatespy">CalendarServer/trunk/txdav/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServertrunktxdavwhotesttest_groupspy">CalendarServer/trunk/txdav/who/test/test_groups.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcalendarservertoolsgatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/gateway.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/gateway.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/calendarserver/tools/gateway.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -48,9 +48,7 @@
</span><span class="cx"> from txdav.who.idirectory import RecordType as CalRecordType
</span><span class="cx"> from twext.who.idirectory import FieldName
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><del>-from txdav.who.delegates import (
- addDelegate, removeDelegate, RecordType as DelegateRecordType
-)
</del><ins>+from txdav.who.delegates import Delegates, RecordType as DelegateRecordType
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> attrMap = {
</span><span class="lines">@@ -529,7 +527,7 @@
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction()
</span><del>- yield addDelegate(txn, record, proxyRecord, (proxyType == "write"))
</del><ins>+ yield Delegates.addDelegate(txn, record, proxyRecord, (proxyType == "write"))
</ins><span class="cx"> yield txn.commit()
</span><span class="cx"> yield self.respondWithProxies(command, record, proxyType)
</span><span class="cx">
</span><span class="lines">@@ -555,7 +553,7 @@
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction()
</span><del>- yield removeDelegate(txn, record, proxyRecord, (proxyType == "write"))
</del><ins>+ yield Delegates.removeDelegate(txn, record, proxyRecord, (proxyType == "write"))
</ins><span class="cx"> yield txn.commit()
</span><span class="cx"> yield self.respondWithProxies(command, record, proxyType)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/principals.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/principals.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/calendarserver/tools/principals.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -32,9 +32,7 @@
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><span class="cx"> from twistedcaldav.config import config
</span><del>-from txdav.who.delegates import (
- addDelegate, removeDelegate, RecordType as DelegateRecordType
-)
</del><ins>+from txdav.who.delegates import Delegates, RecordType as DelegateRecordType
</ins><span class="cx"> from txdav.who.idirectory import AutoScheduleMode
</span><span class="cx"> from txdav.who.groups import GroupCacherPollingWork
</span><span class="cx">
</span><span class="lines">@@ -608,7 +606,7 @@
</span><span class="cx"> print("You are not allowed to add proxies for locations or resources via command line when their proxy assignments come from the directory service.")
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><del>- yield _addRemoveProxy("Added", addDelegate, store, record, proxyType, *proxyIDs)
</del><ins>+ yield _addRemoveProxy("Added", Delegates.addDelegate, store, record, proxyType, *proxyIDs)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -623,9 +621,9 @@
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx"> # Write
</span><del>- yield _addRemoveProxy("Removed", removeDelegate, store, record, "write", *proxyIDs)
</del><ins>+ yield _addRemoveProxy("Removed", Delegates.removeDelegate, store, record, "write", *proxyIDs)
</ins><span class="cx"> # Read
</span><del>- yield _addRemoveProxy("Removed", removeDelegate, store, record, "read", *proxyIDs)
</del><ins>+ yield _addRemoveProxy("Removed", Delegates.removeDelegate, store, record, "read", *proxyIDs)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunkconfcaldavdtestpodAplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/conf/caldavd-test-podA.plist (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/conf/caldavd-test-podA.plist        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/conf/caldavd-test-podA.plist        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -140,7 +140,7 @@
</span><span class="cx">                  <key>Port</key>
</span><span class="cx">                  <integer>11211</integer>
</span><span class="cx">                  </dict>
</span><del>-                 <key>ProxyDB</key>
</del><ins>+                 <key>AllPods</key>
</ins><span class="cx">                  <dict>
</span><span class="cx">                  <key>ClientEnabled</key>
</span><span class="cx">                  <true/>
</span><span class="lines">@@ -153,6 +153,7 @@
</span><span class="cx">                  <key>HandleCacheTypes</key>
</span><span class="cx">                  <array>
</span><span class="cx">                  <string>ProxyDB</string>
</span><ins>+                 <string>DelegatesDB</string>
</ins><span class="cx">                  <string>PrincipalToken</string>
</span><span class="cx">                  <string>DIGESTCREDENTIALS</string>
</span><span class="cx">                  </array>
</span></span></pre></div>
<a id="CalendarServertrunkconfcaldavdtestpodBplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/conf/caldavd-test-podB.plist (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/conf/caldavd-test-podB.plist        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/conf/caldavd-test-podB.plist        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -147,7 +147,7 @@
</span><span class="cx">                  <key>Port</key>
</span><span class="cx">                  <integer>11411</integer>
</span><span class="cx">                  </dict>
</span><del>-                 <key>ProxyDB</key>
</del><ins>+                 <key>AllPods</key>
</ins><span class="cx">                  <dict>
</span><span class="cx">                  <key>ClientEnabled</key>
</span><span class="cx">                  <true/>
</span><span class="lines">@@ -160,6 +160,7 @@
</span><span class="cx">                  <key>HandleCacheTypes</key>
</span><span class="cx">                  <array>
</span><span class="cx">                  <string>ProxyDB</string>
</span><ins>+                 <string>DelegatesDB</string>
</ins><span class="cx">                  <string>PrincipalToken</string>
</span><span class="cx">                  <string>DIGESTCREDENTIALS</string>
</span><span class="cx">                  </array>
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectorycalendaruserproxypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -208,8 +208,12 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def __repr__(self):
+ return "<{}: {}>".format(self.__class__.__name__, str(self))
+
+
</ins><span class="cx"> def __str__(self):
</span><del>- return "%s [%s]" % (self.parent, self.proxyType)
</del><ins>+ return "{} [{}]".format(self.parent, self.proxyType)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def _index(self):
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectorycalendaruserproxyloaderpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxyloader.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxyloader.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxyloader.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -31,7 +31,7 @@
</span><span class="cx"> from twistedcaldav.config import config, fullServerPath
</span><span class="cx"> from twistedcaldav.xmlutil import readXML
</span><span class="cx">
</span><del>-from txdav.who.delegates import addDelegate
</del><ins>+from txdav.who.delegates import Delegates
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -160,13 +160,13 @@
</span><span class="cx"> if delegateRecord is None:
</span><span class="cx"> continue
</span><span class="cx">
</span><del>- yield addDelegate(txn, delegatorRecord, delegateRecord, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegatorRecord, delegateRecord, True)
</ins><span class="cx">
</span><span class="cx"> for proxy in read_proxies:
</span><span class="cx"> delegateRecord = yield directory.recordWithUID(proxy)
</span><span class="cx"> if delegateRecord is None:
</span><span class="cx"> continue
</span><span class="cx">
</span><del>- yield addDelegate(txn, delegatorRecord, delegateRecord, False)
</del><ins>+ yield Delegates.addDelegate(txn, delegatorRecord, delegateRecord, False)
</ins><span class="cx">
</span><span class="cx"> yield txn.commit()
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/principal.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -821,8 +821,12 @@
</span><span class="cx"> ])
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def __repr__(self):
+ return "<{}: {}>".format(self.__class__.__name__, str(self))
+
+
</ins><span class="cx"> def __str__(self):
</span><del>- return "(%s)%s" % (self.record.recordType, self.record.uid)
</del><ins>+ return "({}){}".format(self.record.recordType, self.record.uid)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __eq__(self, other):
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectorytesttest_principalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -32,7 +32,7 @@
</span><span class="cx"> )
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><span class="cx">
</span><del>-from txdav.who.delegates import addDelegate
</del><ins>+from txdav.who.delegates import Delegates
</ins><span class="cx"> from txdav.who.idirectory import AutoScheduleMode, RecordType as CalRecordType
</span><span class="cx"> from txdav.xml import element as davxml
</span><span class="cx">
</span><span class="lines">@@ -1072,8 +1072,8 @@
</span><span class="cx"> self.assertTrue(len((yield principal03.proxyFor(True))) == 0)
</span><span class="cx">
</span><span class="cx"> # Make user01 a read-only proxy for user02 and user03
</span><del>- yield addDelegate(self.transactionUnderTest(), principal02.record, principal01.record, False)
- yield addDelegate(self.transactionUnderTest(), principal03.record, principal01.record, False)
</del><ins>+ yield Delegates.addDelegate(self.transactionUnderTest(), principal02.record, principal01.record, False)
+ yield Delegates.addDelegate(self.transactionUnderTest(), principal03.record, principal01.record, False)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
</span><span class="lines">@@ -1097,7 +1097,15 @@
</span><span class="cx"> self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
</span><span class="cx"> self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
</span><span class="cx">
</span><ins>+ # Remove user01 as read-only proxy for user02 and user03
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal02.record, principal01.record, False)
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal03.record, principal01.record, False)
+ yield self.commit()
</ins><span class="cx">
</span><ins>+ self.assertTrue(len((yield principal01.proxyFor(False))) == 0)
+ self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_isProxyFor(self):
</span><span class="cx"> """
</span><span class="lines">@@ -1119,8 +1127,8 @@
</span><span class="cx"> self.assertFalse((yield principal03.isProxyFor(principal03)))
</span><span class="cx">
</span><span class="cx"> # Make user02 a read-only proxy for user01, and user03 a read-write proxy for user01
</span><del>- yield addDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
- yield addDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
</del><ins>+ yield Delegates.addDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
+ yield Delegates.addDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> # Check proxy for
</span><span class="lines">@@ -1134,7 +1142,26 @@
</span><span class="cx"> self.assertFalse((yield principal03.isProxyFor(principal02)))
</span><span class="cx"> self.assertFalse((yield principal03.isProxyFor(principal03)))
</span><span class="cx">
</span><ins>+ # Remove user02 as read-only proxy for user01, and user03 as read-write proxy for user01
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
+ yield self.commit()
</ins><span class="cx">
</span><ins>+ # Check proxy for
+ proxies = yield principal01.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal01.proxyFor(True)
+ self.assertEqual(proxies, set())
+ proxies = yield principal02.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal02.proxyFor(True)
+ self.assertEqual(proxies, set())
+ proxies = yield principal03.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal03.proxyFor(True)
+ self.assertEqual(proxies, set())
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_proxyMode(self):
</span><span class="cx"> """
</span><span class="lines">@@ -1155,8 +1182,8 @@
</span><span class="cx"> self.assertEqual(mode, "none")
</span><span class="cx">
</span><span class="cx"> # Make user02 a read-only proxy for user01, and user03 a read-write proxy for user01
</span><del>- yield addDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
- yield addDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
</del><ins>+ yield Delegates.addDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
+ yield Delegates.addDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> # Check proxy mode
</span><span class="lines">@@ -1169,7 +1196,26 @@
</span><span class="cx"> mode = yield principal01.proxyMode(principal03)
</span><span class="cx"> self.assertEqual(mode, "none")
</span><span class="cx">
</span><ins>+ # Remove user02 as read-only proxy for user01, and user03 as read-write proxy for user01
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
+ yield self.commit()
</ins><span class="cx">
</span><ins>+ # Check proxy for
+ proxies = yield principal01.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal01.proxyFor(True)
+ self.assertEqual(proxies, set())
+ proxies = yield principal02.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal02.proxyFor(True)
+ self.assertEqual(proxies, set())
+ proxies = yield principal03.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal03.proxyFor(True)
+ self.assertEqual(proxies, set())
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_proxyFor(self):
</span><span class="cx"> """
</span><span class="lines">@@ -1194,8 +1240,8 @@
</span><span class="cx"> self.assertEqual(proxies, set())
</span><span class="cx">
</span><span class="cx"> # Make user02 a read-only proxy for user01, and user03 a read-write proxy for user01
</span><del>- yield addDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
- yield addDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
</del><ins>+ yield Delegates.addDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
+ yield Delegates.addDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> # Check proxy for
</span><span class="lines">@@ -1211,3 +1257,22 @@
</span><span class="cx"> self.assertEqual(proxies, set())
</span><span class="cx"> proxies = yield principal03.proxyFor(True)
</span><span class="cx"> self.assertEqual(proxies, set((principal01,)))
</span><ins>+
+ # Remove user02 as read-only proxy for user01, and user03 as read-write proxy for user01
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal01.record, principal02.record, False)
+ yield Delegates.removeDelegate(self.transactionUnderTest(), principal01.record, principal03.record, True)
+ yield self.commit()
+
+ # Check proxy for
+ proxies = yield principal01.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal01.proxyFor(True)
+ self.assertEqual(proxies, set())
+ proxies = yield principal02.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal02.proxyFor(True)
+ self.assertEqual(proxies, set())
+ proxies = yield principal03.proxyFor(False)
+ self.assertEqual(proxies, set())
+ proxies = yield principal03.proxyFor(True)
+ self.assertEqual(proxies, set())
</ins></span></pre></div>
<a id="CalendarServertrunktwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/stdconfig.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -930,6 +930,7 @@
</span><span class="cx"> # "Port": 11211,
</span><span class="cx"> # "HandleCacheTypes": [
</span><span class="cx"> # "ProxyDB",
</span><ins>+ # "DelegatesDB",
</ins><span class="cx"> # "PrincipalToken",
</span><span class="cx"> # ]
</span><span class="cx"> # },
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavtesttest_upgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/test/test_upgrade.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/test/test_upgrade.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/test/test_upgrade.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -32,7 +32,7 @@
</span><span class="cx"> )
</span><span class="cx"> from txdav.caldav.datastore.index_file import db_basename
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.mailgateway import MailGatewayTokensDatabase
</span><del>-from txdav.who.delegates import delegatesOf
</del><ins>+from txdav.who.delegates import Delegates
</ins><span class="cx"> from txdav.xml.parser import WebDAVDocument
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1470,7 +1470,7 @@
</span><span class="cx"> store = self.storeUnderTest()
</span><span class="cx"> record = yield self.directory.recordWithUID(u"mercury")
</span><span class="cx"> txn = store.newTransaction()
</span><del>- writeDelegates = yield delegatesOf(txn, record, True)
</del><ins>+ writeDelegates = yield Delegates.delegatesOf(txn, record, True)
</ins><span class="cx"> self.assertEquals(len(writeDelegates), 0)
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="lines">@@ -1484,7 +1484,7 @@
</span><span class="cx">
</span><span class="cx"> # Check delegates in store
</span><span class="cx"> txn = store.newTransaction()
</span><del>- writeDelegates = yield delegatesOf(txn, record, True)
</del><ins>+ writeDelegates = yield Delegates.delegatesOf(txn, record, True)
</ins><span class="cx"> self.assertEquals(len(writeDelegates), 1)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set([d.uid for d in writeDelegates]),
</span><span class="lines">@@ -1493,7 +1493,7 @@
</span><span class="cx">
</span><span class="cx"> record = yield self.directory.recordWithUID(u"non_calendar_proxy")
</span><span class="cx">
</span><del>- readDelegates = yield delegatesOf(txn, record, False)
</del><ins>+ readDelegates = yield Delegates.delegatesOf(txn, record, False)
</ins><span class="cx"> self.assertEquals(len(readDelegates), 1)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set([d.uid for d in readDelegates]),
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/upgrade.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/upgrade.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/twistedcaldav/upgrade.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -53,7 +53,7 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.mailgateway import migrateTokensToStore
</span><span class="cx"> from txdav.caldav.datastore.scheduling.scheduler import DirectScheduler
</span><span class="cx"> from txdav.caldav.datastore.util import normalizationLookup
</span><del>-from txdav.who.delegates import addDelegate
</del><ins>+from txdav.who.delegates import Delegates
</ins><span class="cx"> from txdav.who.idirectory import RecordType as CalRecordType
</span><span class="cx"> from txdav.xml import element
</span><span class="cx">
</span><span class="lines">@@ -1078,7 +1078,7 @@
</span><span class="cx"> continue
</span><span class="cx">
</span><span class="cx"> readWrite = (groupType == "calendar-proxy-write")
</span><del>- yield addDelegate(txn, delegatorRecord, delegateRecord, readWrite)
</del><ins>+ yield Delegates.addDelegate(txn, delegatorRecord, delegateRecord, readWrite)
</ins><span class="cx">
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastorepoddingdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/podding/directory.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/podding/directory.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/txdav/common/datastore/podding/directory.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -17,7 +17,7 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from twisted.python.reflect import namedClass
</span><span class="cx"> from txdav.common.datastore.podding.base import FailedCrossPodRequestError
</span><del>-from txdav.who.delegates import _delegatesOfUIDs, _delegatedToUIDs, setDelegates
</del><ins>+from txdav.who.delegates import Delegates
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> class DirectoryPoddingConduitMixin(object):
</span><span class="lines">@@ -81,7 +81,7 @@
</span><span class="cx"> raise FailedCrossPodRequestError("Cross-pod delegate missing on this server: {}".format(uid))
</span><span class="cx"> delegates.append(delegate)
</span><span class="cx">
</span><del>- yield setDelegates(txn, delegator, delegates, request["read-write"])
</del><ins>+ yield Delegates.setDelegates(txn, delegator, delegates, request["read-write"])
</ins><span class="cx"> except Exception as e:
</span><span class="cx"> returnValue({
</span><span class="cx"> "result": "exception",
</span><span class="lines">@@ -140,7 +140,7 @@
</span><span class="cx"> if delegator is None or not delegator.thisServer():
</span><span class="cx"> raise FailedCrossPodRequestError("Cross-pod delegate not on this server: {}".format(delegator.uid))
</span><span class="cx">
</span><del>- delegates = yield _delegatesOfUIDs(txn, delegator, request["read-write"], request["expanded"])
</del><ins>+ delegates = yield Delegates._delegatesOfUIDs(txn, delegator, request["read-write"], request["expanded"])
</ins><span class="cx"> except Exception as e:
</span><span class="cx"> returnValue({
</span><span class="cx"> "result": "exception",
</span><span class="lines">@@ -201,7 +201,7 @@
</span><span class="cx"> if delegate is None or delegate.thisServer():
</span><span class="cx"> raise FailedCrossPodRequestError("Cross-pod delegate missing or on this server: {}".format(delegate.uid))
</span><span class="cx">
</span><del>- delegateors = yield _delegatedToUIDs(txn, delegate, request["read-write"], onlyThisServer=True)
</del><ins>+ delegateors = yield Delegates._delegatedToUIDs(txn, delegate, request["read-write"], onlyThisServer=True)
</ins><span class="cx"> except Exception as e:
</span><span class="cx"> returnValue({
</span><span class="cx"> "result": "exception",
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/sql.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/sql.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -80,6 +80,7 @@
</span><span class="cx"> from txdav.common.inotifications import INotificationCollection, \
</span><span class="cx"> INotificationObject
</span><span class="cx"> from txdav.idav import ChangeCategory
</span><ins>+from txdav.who.delegates import Delegates
</ins><span class="cx"> from txdav.xml import element
</span><span class="cx">
</span><span class="cx"> from uuid import uuid4, UUID
</span><span class="lines">@@ -1103,12 +1104,12 @@
</span><span class="cx"> if record is None:
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><del>- groupID = yield self._addGroupQuery.on(
</del><ins>+ groupID = (yield self._addGroupQuery.on(
</ins><span class="cx"> self,
</span><span class="cx"> name=name.encode("utf-8"),
</span><span class="cx"> groupUID=groupUID.encode("utf-8"),
</span><span class="cx"> membershipHash=membershipHash
</span><del>- )
</del><ins>+ ))[0][0]
</ins><span class="cx">
</span><span class="cx"> yield self.refreshGroup(
</span><span class="cx"> groupUID, record, groupID, name.encode("utf-8"), membershipHash, True
</span><span class="lines">@@ -1184,7 +1185,7 @@
</span><span class="cx"> bool(results[0][4]), # extant
</span><span class="cx"> ))
</span><span class="cx"> else:
</span><del>- raise
</del><ins>+ returnValue((None, None, None, None, None))
</ins><span class="cx"> else:
</span><span class="cx"> yield savepoint.release(self)
</span><span class="cx"> results = (
</span><span class="lines">@@ -1201,7 +1202,7 @@
</span><span class="cx"> bool(results[0][4]), # extant
</span><span class="cx"> ))
</span><span class="cx"> else:
</span><del>- raise
</del><ins>+ returnValue((None, None, None, None, None))
</ins><span class="cx"> else:
</span><span class="cx"> returnValue((None, None, None, None, None))
</span><span class="cx">
</span><span class="lines">@@ -1375,33 +1376,58 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if membershipChanged:
</span><del>- newMemberUIDs = set()
- for memberUID in memberUIDs:
- newMemberUIDs.add(memberUID)
- yield self.synchronizeMembers(groupID, newMemberUIDs)
</del><ins>+ yield self.synchronizeMembers(groupID, set(memberUIDs))
</ins><span class="cx">
</span><span class="cx"> returnValue(membershipChanged)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def synchronizeMembers(self, groupID, newMemberUIDs):
</span><ins>+ """
+ Update the group membership table in the database to match the new membership list. This
+ method will diff the existing set with the new set and apply the changes. It also calls out
+ to a groupChanged() method with the set of added and removed members so that other modules
+ that depend on groups can monitor the changes.
+
+ @param groupID: group id of group to update
+ @type groupID: L{str}
+ @param newMemberUIDs: set of new member UIDs in the group
+ @type newMemberUIDs: L{set} of L{str}
+ """
</ins><span class="cx"> numRemoved = numAdded = 0
</span><span class="cx"> cachedMemberUIDs = (yield self.groupMemberUIDs(groupID))
</span><span class="cx">
</span><del>- for memberUID in cachedMemberUIDs:
- if memberUID not in newMemberUIDs:
- numRemoved += 1
- yield self.removeMemberFromGroup(memberUID, groupID)
</del><ins>+ removed = cachedMemberUIDs - newMemberUIDs
+ for memberUID in removed:
+ numRemoved += 1
+ yield self.removeMemberFromGroup(memberUID, groupID)
</ins><span class="cx">
</span><del>- for memberUID in newMemberUIDs:
- if memberUID not in cachedMemberUIDs:
- numAdded += 1
- yield self.addMemberToGroup(memberUID, groupID)
</del><ins>+ added = newMemberUIDs - cachedMemberUIDs
+ for memberUID in added:
+ numAdded += 1
+ yield self.addMemberToGroup(memberUID, groupID)
</ins><span class="cx">
</span><ins>+ yield self.groupChanged(groupID, added, removed)
+
</ins><span class="cx"> returnValue((numAdded, numRemoved))
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><ins>+ def groupChanged(self, groupID, addedUIDs, removedUIDs):
+ """
+ Called when membership of a group changes.
+
+ @param groupID: group id of group that changed
+ @type groupID: L{str}
+ @param addedUIDs: set of new member UIDs added to the group
+ @type addedUIDs: L{set} of L{str}
+ @param removedUIDs: set of old member UIDs removed from the group
+ @type removedUIDs: L{set} of L{str}
+ """
+ yield Delegates.groupChanged(self, groupID, addedUIDs, removedUIDs)
+
+
+ @inlineCallbacks
</ins><span class="cx"> def groupMembers(self, groupID):
</span><span class="cx"> """
</span><span class="cx"> The members of the given group as recorded in the db
</span><span class="lines">@@ -1826,41 +1852,40 @@
</span><span class="cx"> @rtype: a Deferred resulting in a set
</span><span class="cx"> """
</span><span class="cx"> delegates = set()
</span><ins>+ delegatorU = delegator.encode("utf-8")
</ins><span class="cx">
</span><span class="cx"> # First get the direct delegates
</span><span class="cx"> results = (
</span><span class="cx"> yield self._selectDelegatesQuery.on(
</span><span class="cx"> self,
</span><del>- delegator=delegator.encode("utf-8"),
</del><ins>+ delegator=delegatorU,
</ins><span class="cx"> readWrite=1 if readWrite else 0
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>- for row in results:
- delegates.add(row[0].decode("utf-8"))
</del><ins>+ delegates.update([row[0].decode("utf-8") for row in results])
</ins><span class="cx">
</span><span class="cx"> if expanded:
</span><span class="cx"> # Get those who are in groups which have been delegated to
</span><span class="cx"> results = (
</span><span class="cx"> yield self._selectIndirectDelegatesQuery.on(
</span><span class="cx"> self,
</span><del>- delegator=delegator.encode("utf-8"),
</del><ins>+ delegator=delegatorU,
</ins><span class="cx"> readWrite=1 if readWrite else 0
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>- for row in results:
- delegates.add(row[0].decode("utf-8"))
</del><ins>+ # Skip the delegator if they are in one of the groups
+ delegates.update([row[0].decode("utf-8") for row in results if row[0] != delegatorU])
</ins><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> # Get the directly-delegated-to groups
</span><span class="cx"> results = (
</span><span class="cx"> yield self._selectDelegateGroupsQuery.on(
</span><span class="cx"> self,
</span><del>- delegator=delegator.encode("utf-8"),
</del><ins>+ delegator=delegatorU,
</ins><span class="cx"> readWrite=1 if readWrite else 0
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>- for row in results:
- delegates.add(row[0].decode("utf-8"))
</del><ins>+ delegates.update([row[0].decode("utf-8") for row in results])
</ins><span class="cx">
</span><span class="cx"> returnValue(delegates)
</span><span class="cx">
</span><span class="lines">@@ -1881,29 +1906,29 @@
</span><span class="cx"> @rtype: a Deferred resulting in a set
</span><span class="cx"> """
</span><span class="cx"> delegators = set()
</span><ins>+ delegateU = delegate.encode("utf-8")
</ins><span class="cx">
</span><span class="cx"> # First get the direct delegators
</span><span class="cx"> results = (
</span><span class="cx"> yield self._selectDirectDelegatorsQuery.on(
</span><span class="cx"> self,
</span><del>- delegate=delegate.encode("utf-8"),
</del><ins>+ delegate=delegateU,
</ins><span class="cx"> readWrite=1 if readWrite else 0
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>- for row in results:
- delegators.add(row[0].decode("utf-8"))
</del><ins>+ delegators.update([row[0].decode("utf-8") for row in results])
</ins><span class="cx">
</span><span class="cx"> # Finally get those who have delegated to groups the delegate
</span><span class="cx"> # is a member of
</span><span class="cx"> results = (
</span><span class="cx"> yield self._selectIndirectDelegatorsQuery.on(
</span><span class="cx"> self,
</span><del>- delegate=delegate.encode("utf-8"),
</del><ins>+ delegate=delegateU,
</ins><span class="cx"> readWrite=1 if readWrite else 0
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>- for row in results:
- delegators.add(row[0].decode("utf-8"))
</del><ins>+ # Skip the delegator if they are in one of the groups
+ delegators.update([row[0].decode("utf-8") for row in results if row[0] != delegateU])
</ins><span class="cx">
</span><span class="cx"> returnValue(delegators)
</span><span class="cx">
</span><span class="lines">@@ -1924,7 +1949,6 @@
</span><span class="cx"> @rtype: a Deferred resulting in a set
</span><span class="cx">
</span><span class="cx"> """
</span><del>- delegators = set()
</del><span class="cx"> results = (
</span><span class="cx"> yield self._selectDelegatorsToGroupQuery.on(
</span><span class="cx"> self,
</span><span class="lines">@@ -1932,8 +1956,7 @@
</span><span class="cx"> readWrite=1 if readWrite else 0
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>- for row in results:
- delegators.add(row[0].decode("utf-8"))
</del><ins>+ delegators = set([row[0].decode("utf-8") for row in results])
</ins><span class="cx"> returnValue(delegators)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavdpsserverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/dps/server.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/dps/server.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/txdav/dps/server.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> from twisted.protocols import amp
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="cx"> from twisted.python.usage import Options, UsageError
</span><ins>+from twistedcaldav import memcachepool
</ins><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
</span><span class="cx"> from txdav.dps.commands import (
</span><span class="lines">@@ -796,6 +797,14 @@
</span><span class="cx"> raise
</span><span class="cx">
</span><span class="cx">
</span><ins>+ #
+ # Configure Memcached Client Pool
+ #
+ memcachepool.installPools(
+ config.Memcached.Pools,
+ config.Memcached.MaxClients,
+ )
+
</ins><span class="cx"> log.info("Created directory service")
</span><span class="cx">
</span><span class="cx"> return strPortsService(
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/delegates.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/delegates.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/txdav/who/delegates.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -24,6 +24,7 @@
</span><span class="cx"> DeferredList
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.config import config
</span><ins>+from twistedcaldav.memcacher import Memcacher
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.who.idirectory import (
</span><span class="lines">@@ -78,11 +79,11 @@
</span><span class="cx"> RecordType.readDelegateGroup, RecordType.writeDelegateGroup
</span><span class="cx"> ): # Members are delegates of this record
</span><span class="cx"> readWrite = (self.recordType is RecordType.writeDelegateGroup)
</span><del>- delegateUIDs = yield _delegatesOfUIDs(txn, parentRecord, readWrite, expanded=expanded)
</del><ins>+ delegateUIDs = yield Delegates._delegatesOfUIDs(txn, parentRecord, readWrite, expanded=expanded)
</ins><span class="cx">
</span><span class="cx"> else: # Members have delegated to this record
</span><span class="cx"> readWrite = (self.recordType is RecordType.writeDelegatorGroup)
</span><del>- delegateUIDs = yield _delegatedToUIDs(txn, parentRecord, readWrite)
</del><ins>+ delegateUIDs = yield Delegates._delegatedToUIDs(txn, parentRecord, readWrite)
</ins><span class="cx"> returnValue(delegateUIDs)
</span><span class="cx">
</span><span class="cx"> delegateUIDs = yield self.service._store.inTransaction(
</span><span class="lines">@@ -130,7 +131,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> def _setMembers(txn):
</span><del>- return setDelegates(txn, delegator, memberRecords, readWrite)
</del><ins>+ return Delegates.setDelegates(txn, delegator, memberRecords, readWrite)
</ins><span class="cx">
</span><span class="cx"> yield self.service._store.inTransaction(
</span><span class="cx"> "DirectoryRecord.setMembers", _setMembers
</span><span class="lines">@@ -227,249 +228,398 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-@inlineCallbacks
-def setDelegates(txn, delegator, delegates, readWrite):
</del><ins>+class CachingDelegates(object):
</ins><span class="cx"> """
</span><del>- Sets the full set of delegates for a delegator.
</del><ins>+ Manages access to the store's delegates API, including caching of results.
+ """
</ins><span class="cx">
</span><del>- We need to take multiple pods into account by re-directing this request
- to the cross-pod conduit if the delegator is not local to this pod.
</del><ins>+ class DelegatesMemcacher(Memcacher):
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param delegates: the delegates directory records
- @type delegates: L{list}} of L{IDirectoryRecord}
- @param readWrite: if True, read and write access is granted; read-only
- access otherwise
- """
- if delegator.thisServer():
- yield txn.removeDelegates(delegator.uid, readWrite)
- yield txn.removeDelegateGroups(delegator.uid, readWrite)
</del><ins>+ def __init__(self, namespace):
+ super(CachingDelegates.DelegatesMemcacher, self).__init__(namespace, key_normalization=True)
</ins><span class="cx">
</span><del>- for delegate in delegates:
- yield addDelegate(txn, delegator, delegate, readWrite)
- else:
- yield _podSetDelegates(txn, delegator, delegates, readWrite)
</del><ins>+ def _key(self, keyname, uid, readWrite, expanded):
+ return "{}{}:{}#{}".format(
+ keyname,
+ "-expanded" if expanded else "",
+ uid,
+ "write" if readWrite else "read",
+ )
</ins><span class="cx">
</span><ins>+ def _membersKey(self, uid, readWrite, expanded):
+ return self._key("members", uid, readWrite, expanded)
</ins><span class="cx">
</span><ins>+ def _membershipsKey(self, uid, readWrite):
+ return self._key("memberships", uid, readWrite, False)
</ins><span class="cx">
</span><del>-@inlineCallbacks
-def addDelegate(txn, delegator, delegate, readWrite):
- """
- Adds "delegate" as a delegate of "delegator". The type of access is
- specified by the "readWrite" parameter.
</del><ins>+ def setMembers(self, uid, readWrite, members, expanded):
+ return self.set(
+ self._membersKey(uid, readWrite, expanded),
+ ",".join(members),
+ )
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param delegate: the delegate's directory record
- @type delegate: L{IDirectoryRecord}
- @param readWrite: if True, read and write access is granted; read-only
- access otherwise
- """
- if delegate.recordType == BaseRecordType.group:
- # find the groupID
- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- _ignore_extant
- ) = yield txn.groupByUID(
- delegate.uid
- )
- yield txn.addDelegateGroup(delegator.uid, groupID, readWrite)
- else:
- yield txn.addDelegate(delegator.uid, delegate.uid, readWrite)
</del><ins>+ def setMemberships(self, uid, readWrite, memberships):
+ return self.set(
+ self._membershipsKey(uid, readWrite),
+ ",".join(memberships),
+ )
</ins><span class="cx">
</span><ins>+ @staticmethod
+ def _value_decode(value):
+ if value:
+ return set(value.split(","))
+ elif value is None:
+ return None
+ else:
+ return set()
</ins><span class="cx">
</span><ins>+ @inlineCallbacks
+ def getMembers(self, uid, readWrite, expanded):
+ value = yield self.get(self._membersKey(uid, readWrite, expanded))
+ returnValue(self._value_decode(value))
</ins><span class="cx">
</span><del>-@inlineCallbacks
-def removeDelegate(txn, delegator, delegate, readWrite):
- """
- Removes "delegate" as a delegate of "delegator". The type of access is
- specified by the "readWrite" parameter.
</del><ins>+ @inlineCallbacks
+ def getMemberships(self, uid, readWrite):
+ value = yield self.get(self._membershipsKey(uid, readWrite))
+ returnValue(self._value_decode(value))
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param delegate: the delegate's directory record
- @type delegate: L{IDirectoryRecord}
- @param readWrite: if True, read and write access is revoked; read-only
- access otherwise
- """
- if delegate.recordType == BaseRecordType.group:
- # find the groupID
- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- _ignore_extant
- ) = yield txn.groupByUID(
- delegate.uid
- )
- yield txn.removeDelegateGroup(delegator.uid, groupID, readWrite)
- else:
- yield txn.removeDelegate(delegator.uid, delegate.uid, readWrite)
</del><ins>+ @inlineCallbacks
+ def deleteMember(self, uid, readWrite):
+ """
+ Delete both the regular and expanded keys.
+ """
+ yield self.delete(self._membersKey(uid, readWrite, False))
+ yield self.delete(self._membersKey(uid, readWrite, True))
</ins><span class="cx">
</span><ins>+ @inlineCallbacks
+ def deleteMembership(self, uid, readWrite):
+ """
+ Delete both the regular and expanded keys.
+ """
+ yield self.delete(self._membershipsKey(uid, readWrite))
</ins><span class="cx">
</span><span class="cx">
</span><del>-@inlineCallbacks
-def delegatesOf(txn, delegator, readWrite, expanded=False):
- """
- Return the records of the delegates of "delegator". The type of access
- is specified by the "readWrite" parameter.
</del><ins>+ def __init__(self):
+ self._memcacher = CachingDelegates.DelegatesMemcacher("DelegatesDB")
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param readWrite: if True, read and write access delegates are returned;
- read-only access otherwise
- @return: the set of directory records
- @rtype: a Deferred which fires a set of L{IDirectoryRecord}
- """
- delegateUIDs = yield _delegatesOfUIDs(txn, delegator, readWrite, expanded)
</del><span class="cx">
</span><del>- records = []
- directory = delegator.service
- for uid in delegateUIDs:
- if uid != delegator.uid:
- record = (yield directory.recordWithUID(uid))
- if record is not None:
- records.append(record)
- returnValue(records)
</del><ins>+ @inlineCallbacks
+ def setDelegates(self, txn, delegator, delegates, readWrite):
+ """
+ Sets the full set of delegates for a delegator.
</ins><span class="cx">
</span><ins>+ We need to take multiple pods into account by re-directing this request
+ to the cross-pod conduit if the delegator is not local to this pod.
</ins><span class="cx">
</span><ins>+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param delegates: the delegates directory records
+ @type delegates: L{list}} of L{IDirectoryRecord}
+ @param readWrite: if True, read and write access is granted; read-only
+ access otherwise
+ """
+ existingDelegates = yield self.delegatesOf(txn, delegator, readWrite)
</ins><span class="cx">
</span><del>-@inlineCallbacks
-def delegatedTo(txn, delegate, readWrite):
- """
- Return the records of those who have delegated to "delegate". The type of
- access is specified by the "readWrite" parameter.
</del><ins>+ if delegator.thisServer():
+ # Remove some
+ for delegate in set(existingDelegates) - set(delegates):
+ yield self.removeDelegate(txn, delegator, delegate, readWrite)
</ins><span class="cx">
</span><del>- @param delegate: the delegate's directory record
- @type delegate: L{IDirectoryRecord}
- @param readWrite: if True, read and write access delegators are returned;
- read-only access otherwise
- @return: the set of directory records
- @rtype: a Deferred which fires a set of L{IDirectoryRecord}
- """
- delegatorUIDs = yield _delegatedToUIDs(txn, delegate, readWrite)
</del><ins>+ for delegate in set(delegates) - set(existingDelegates):
+ yield self.addDelegate(txn, delegator, delegate, readWrite)
+ else:
+ yield self._podSetDelegates(txn, delegator, delegates, readWrite)
</ins><span class="cx">
</span><del>- records = []
- directory = delegate.service
- for uid in delegatorUIDs:
- if uid != delegate.uid:
- record = (yield directory.recordWithUID(uid))
- if record is not None:
- records.append(record)
- returnValue(records)
</del><span class="cx">
</span><ins>+ @inlineCallbacks
+ def addDelegate(self, txn, delegator, delegate, readWrite):
+ """
+ Adds "delegate" as a delegate of "delegator". The type of access is
+ specified by the "readWrite" parameter.
</ins><span class="cx">
</span><ins>+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param delegate: the delegate's directory record
+ @type delegate: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access is granted; read-only
+ access otherwise
+ """
</ins><span class="cx">
</span><del>-@inlineCallbacks
-def _delegatesOfUIDs(txn, delegator, readWrite, expanded=False):
- """
- Return the UIDs of the delegates of "delegator". The type of access
- is specified by the "readWrite" parameter.
</del><ins>+ # Never add the delegator as a delegate
+ if delegator.uid == delegate.uid:
+ returnValue(None)
</ins><span class="cx">
</span><del>- We need to take multiple pods into account by re-directing this request
- to the cross-pod conduit if the delegator is not local to this pod.
</del><ins>+ existingDelegateUIDs = yield self._delegatesOfUIDs(txn, delegator, readWrite, expanded=True)
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param readWrite: if True, read and write access delegates are returned;
- read-only access otherwise
- @return: the set of directory record uids
- @rtype: a Deferred which fires a set of L{str}
- """
</del><ins>+ if delegate.recordType == BaseRecordType.group:
+ # find the groupID
+ (
+ groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+ _ignore_extant
+ ) = yield txn.groupByUID(
+ delegate.uid
+ )
+ yield txn.addDelegateGroup(delegator.uid, groupID, readWrite)
+ else:
+ yield txn.addDelegate(delegator.uid, delegate.uid, readWrite)
</ins><span class="cx">
</span><del>- log.debug("_delegatesOfUIDs for: {} and read-write = {} and expanded = {}".format(delegator.uid, readWrite, expanded,))
- if delegator.thisServer():
- delegateUIDs = yield txn.delegates(delegator.uid, readWrite, expanded=expanded)
- else:
- delegateUIDs = yield _podDelegates(txn, delegator, readWrite, expanded=expanded)
- returnValue(delegateUIDs)
</del><ins>+ # Update cache (remove the member cache entry first as we need to recalculate it for
+ # memberships removal)
+ yield self._memcacher.deleteMember(delegator.uid, readWrite)
+ newDelegateUIDs = yield self._delegatesOfUIDs(txn, delegator, readWrite, expanded=True)
+ for uid in set(newDelegateUIDs) - set(existingDelegateUIDs):
+ yield self._memcacher.deleteMembership(uid, readWrite)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
+ def removeDelegate(self, txn, delegator, delegate, readWrite):
+ """
+ Removes "delegate" as a delegate of "delegator". The type of access is
+ specified by the "readWrite" parameter.
</ins><span class="cx">
</span><del>-@inlineCallbacks
-def _delegatedToUIDs(txn, delegate, readWrite, onlyThisServer=False):
- """
- Return the UIDs of those who have delegated to "delegate". The type of
- access is specified by the "readWrite" parameter.
</del><ins>+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param delegate: the delegate's directory record
+ @type delegate: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access is revoked; read-only
+ access otherwise
+ """
</ins><span class="cx">
</span><del>- We need to take multiple pods into account by re-directing this request
- to the cross-pod conduit if the delegate is not local to this pod.
</del><ins>+ # Never remove the delegator as a delegate
+ if delegator.uid == delegate.uid:
+ returnValue(None)
</ins><span class="cx">
</span><del>- @param delegate: the delegate's directory record
- @type delegate: L{IDirectoryRecord}
- @param readWrite: if True, read and write access delegators are returned;
- read-only access otherwise
- @param onlyThisServer: used when doing the query as part of a cross-pod request since that
- should only returns results for this server
- @type onlyThisServer: L{bool}
- @return: the set of directory record uids
- @rtype: a Deferred which fires a set of L{str}
- """
</del><ins>+ existingDelegateUIDs = yield self._delegatesOfUIDs(txn, delegator, readWrite, expanded=True)
</ins><span class="cx">
</span><ins>+ if delegate.recordType == BaseRecordType.group:
+ # find the groupID
+ (
+ groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+ _ignore_extant
+ ) = yield txn.groupByUID(
+ delegate.uid
+ )
+ yield txn.removeDelegateGroup(delegator.uid, groupID, readWrite)
+ else:
+ yield txn.removeDelegate(delegator.uid, delegate.uid, readWrite)
</ins><span class="cx">
</span><del>- log.debug("_delegatedToUIDs for: {} and read-write = {}".format(delegate.uid, readWrite,))
- delegatorUIDs = (yield txn.delegators(delegate.uid, readWrite))
- if not onlyThisServer and config.Servers.Enabled:
- delegatorUIDs.update((yield _podDelegators(txn, delegate, readWrite)))
- returnValue(delegatorUIDs)
</del><ins>+ # Update cache (remove the member cache entry first as we need to recalculate it for
+ # memberships removal)
+ yield self._memcacher.deleteMember(delegator.uid, readWrite)
+ newDelegateUIDs = yield self._delegatesOfUIDs(txn, delegator, readWrite, expanded=True)
+ for uid in set(existingDelegateUIDs) - set(newDelegateUIDs):
+ yield self._memcacher.deleteMembership(uid, readWrite)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
+ def groupChanged(self, txn, groupID, addedUIDs, removedUIDs):
+ """
+ A group has changed. We need to see which delegators might be using this group
+ and invalidate caches.
</ins><span class="cx">
</span><del>-def _podSetDelegates(txn, delegator, delegates, readWrite):
- """
- Sets the full set of delegates for a delegator.
</del><ins>+ @param groupID: group id of group that changed
+ @type groupID: L{str}
+ @param addedUIDs: set of new member UIDs added to the group
+ @type addedUIDs: L{set} of L{str}
+ @param removedUIDs: set of old member UIDs removed from the group
+ @type removedUIDs: L{set} of L{str}
+ """
</ins><span class="cx">
</span><del>- We need to take multiple pods into account by re-directing this request
- to the cross-pod conduit if the delegator is not local to this pod.
</del><ins>+ # Remove member cache entry for delegators using the group
+ delegators = set()
+ for readWrite in (True, False):
+ delegators.update((yield txn.delegatorsToGroup(groupID, readWrite)))
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param delegates: the delegates directory records
- @type delegates: L{list}} of L{IDirectoryRecord}
- @param readWrite: if True, read and write access is granted; read-only
- access otherwise
- """
- return txn.store().conduit.send_set_delegates(txn, delegator, delegates, readWrite)
</del><ins>+ for delegator in delegators:
+ yield self._memcacher.deleteMember(delegator, True)
+ yield self._memcacher.deleteMember(delegator, False)
</ins><span class="cx">
</span><ins>+ # Remove membership cache entries for added/removed delegates
+ for delegate in (addedUIDs | removedUIDs):
+ yield self._memcacher.deleteMembership(delegate, True)
+ yield self._memcacher.deleteMembership(delegate, False)
</ins><span class="cx">
</span><span class="cx">
</span><del>-def _podDelegates(txn, delegator, readWrite, expanded=False):
- """
- Do a cross-pod request to get the delegates for this delegator.
</del><ins>+ @inlineCallbacks
+ def delegatesOf(self, txn, delegator, readWrite, expanded=False):
+ """
+ Return the records of the delegates of "delegator". The type of access
+ is specified by the "readWrite" parameter.
</ins><span class="cx">
</span><del>- @param delegator: the delegator's directory record
- @type delegator: L{IDirectoryRecord}
- @param readWrite: if True, read and write access delegates are returned;
- read-only access otherwise
- @return: the set of directory record uids
- @rtype: a Deferred which fires a set of L{str}
- """
</del><ins>+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access delegates are returned;
+ read-only access otherwise
+ @return: the set of directory records
+ @rtype: a Deferred which fires a set of L{IDirectoryRecord}
+ """
+ delegateUIDs = yield self._delegatesOfUIDs(txn, delegator, readWrite, expanded)
</ins><span class="cx">
</span><del>- log.debug("_podDelegates for: {} and read-write = {} and expanded = {}".format(delegator.uid, readWrite, expanded,))
- return txn.store().conduit.send_get_delegates(txn, delegator, readWrite, expanded)
</del><ins>+ records = []
+ directory = delegator.service
+ for uid in delegateUIDs:
+ if uid != delegator.uid:
+ record = (yield directory.recordWithUID(uid))
+ if record is not None:
+ records.append(record)
+ returnValue(records)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
+ def delegatedTo(self, txn, delegate, readWrite):
+ """
+ Return the records of those who have delegated to "delegate". The type of
+ access is specified by the "readWrite" parameter.
</ins><span class="cx">
</span><del>-@inlineCallbacks
-def _podDelegators(txn, delegate, readWrite):
- """
- Do a cross-pod request to get the delegators for this delegate. We need to iterate over all
- other pod servers to get results from each one.
</del><ins>+ @param delegate: the delegate's directory record
+ @type delegate: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access delegators are returned;
+ read-only access otherwise
+ @return: the set of directory records
+ @rtype: a Deferred which fires a set of L{IDirectoryRecord}
+ """
+ delegatorUIDs = yield self._delegatedToUIDs(txn, delegate, readWrite)
</ins><span class="cx">
</span><del>- @param delegate: the delegate's directory record
- @type delegate: L{IDirectoryRecord}
- @param readWrite: if True, read and write access delegates are returned;
- read-only access otherwise
- @return: the set of directory record uids
- @rtype: a Deferred which fires a set of L{str}
- """
</del><ins>+ records = []
+ directory = delegate.service
+ for uid in delegatorUIDs:
+ if uid != delegate.uid:
+ record = (yield directory.recordWithUID(uid))
+ if record is not None:
+ records.append(record)
+ returnValue(records)
</ins><span class="cx">
</span><del>- log.debug("_podDelegators for: {} and read-write = {}".format(delegate.uid, readWrite,))
- results = yield DeferredList([
- txn.store().conduit.send_get_delegators(txn, server, delegate, readWrite) for
- server in txn.directoryService().serversDB.allServersExceptThis()
- ], consumeErrors=True)
- delegators = set()
- for result in results:
- if result and result[0]:
- delegators.update(result[1])
- returnValue(delegators)
</del><ins>+
+ @inlineCallbacks
+ def _delegatesOfUIDs(self, txn, delegator, readWrite, expanded=False):
+ """
+ Return the UIDs of the delegates of "delegator". The type of access
+ is specified by the "readWrite" parameter.
+
+ We need to take multiple pods into account by re-directing this request
+ to the cross-pod conduit if the delegator is not local to this pod.
+
+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access delegates are returned;
+ read-only access otherwise
+ @return: the set of directory record uids
+ @rtype: a Deferred which fires a set of L{str}
+ """
+
+ # Try cache first
+ delegateUIDs = yield self._memcacher.getMembers(delegator.uid, readWrite, expanded)
+ if delegateUIDs is not None:
+ log.debug("_delegatesOfUIDs cached for: {} and read-write = {} and expanded = {}".format(delegator.uid, readWrite, expanded,))
+ returnValue(delegateUIDs)
+
+ # Get from the store
+ log.debug("_delegatesOfUIDs for: {} and read-write = {} and expanded = {}".format(delegator.uid, readWrite, expanded,))
+ if delegator.thisServer():
+ delegateUIDs = yield txn.delegates(delegator.uid, readWrite, expanded=expanded)
+
+ # Cache result - only need to do this on the host
+ yield self._memcacher.setMembers(delegator.uid, readWrite, delegateUIDs, expanded)
+ else:
+ delegateUIDs = yield self._podDelegates(txn, delegator, readWrite, expanded=expanded)
+
+ returnValue(delegateUIDs)
+
+
+ @inlineCallbacks
+ def _delegatedToUIDs(self, txn, delegate, readWrite, onlyThisServer=False):
+ """
+ Return the UIDs of those who have delegated to "delegate". The type of
+ access is specified by the "readWrite" parameter.
+
+ We need to take multiple pods into account by re-directing this request
+ to the cross-pod conduit if the delegate is not local to this pod.
+
+ @param delegate: the delegate's directory record
+ @type delegate: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access delegators are returned;
+ read-only access otherwise
+ @param onlyThisServer: used when doing the query as part of a cross-pod request since that
+ should only returns results for this server
+ @type onlyThisServer: L{bool}
+ @return: the set of directory record uids
+ @rtype: a Deferred which fires a set of L{str}
+ """
+
+ # Try cache first
+ delegatorUIDs = yield self._memcacher.getMemberships(delegate.uid, readWrite)
+ if delegatorUIDs is not None:
+ log.debug("_delegatedToUIDs cached for: {} and read-write = {}".format(delegate.uid, readWrite,))
+ returnValue(delegatorUIDs)
+
+ # Get from the store
+ log.debug("_delegatedToUIDs for: {} and read-write = {}".format(delegate.uid, readWrite,))
+ delegatorUIDs = (yield txn.delegators(delegate.uid, readWrite))
+ if not onlyThisServer and config.Servers.Enabled:
+ delegatorUIDs.update((yield self._podDelegators(txn, delegate, readWrite)))
+
+ # Cache result - only need to do this on the host
+ yield self._memcacher.setMemberships(delegate.uid, readWrite, delegatorUIDs)
+
+ returnValue(delegatorUIDs)
+
+
+ def _podSetDelegates(self, txn, delegator, delegates, readWrite):
+ """
+ Sets the full set of delegates for a delegator.
+
+ We need to take multiple pods into account by re-directing this request
+ to the cross-pod conduit if the delegator is not local to this pod.
+
+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param delegates: the delegates directory records
+ @type delegates: L{list}} of L{IDirectoryRecord}
+ @param readWrite: if True, read and write access is granted; read-only
+ access otherwise
+ """
+ return txn.store().conduit.send_set_delegates(txn, delegator, delegates, readWrite)
+
+
+ def _podDelegates(self, txn, delegator, readWrite, expanded=False):
+ """
+ Do a cross-pod request to get the delegates for this delegator.
+
+ @param delegator: the delegator's directory record
+ @type delegator: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access delegates are returned;
+ read-only access otherwise
+ @return: the set of directory record uids
+ @rtype: a Deferred which fires a set of L{str}
+ """
+
+ log.debug("_podDelegates for: {} and read-write = {} and expanded = {}".format(delegator.uid, readWrite, expanded,))
+ return txn.store().conduit.send_get_delegates(txn, delegator, readWrite, expanded)
+
+
+ @inlineCallbacks
+ def _podDelegators(self, txn, delegate, readWrite):
+ """
+ Do a cross-pod request to get the delegators for this delegate. We need to iterate over all
+ other pod servers to get results from each one.
+
+ @param delegate: the delegate's directory record
+ @type delegate: L{IDirectoryRecord}
+ @param readWrite: if True, read and write access delegates are returned;
+ read-only access otherwise
+ @return: the set of directory record uids
+ @rtype: a Deferred which fires a set of L{str}
+ """
+
+ log.debug("_podDelegators for: {} and read-write = {}".format(delegate.uid, readWrite,))
+ results = yield DeferredList([
+ txn.store().conduit.send_get_delegators(txn, server, delegate, readWrite) for
+ server in txn.directoryService().serversDB.allServersExceptThis()
+ ], consumeErrors=True)
+ delegators = set()
+ for result in results:
+ if result and result[0]:
+ delegators.update(result[1])
+ returnValue(delegators)
+
+Delegates = CachingDelegates()
</ins></span></pre></div>
<a id="CalendarServertrunktxdavwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/test/test_delegates.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/test/test_delegates.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/txdav/who/test/test_delegates.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -18,10 +18,8 @@
</span><span class="cx"> Delegates implementation tests
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from txdav.who.delegates import (
- addDelegate, removeDelegate, delegatesOf, delegatedTo,
- RecordType as DelegateRecordType
-)
</del><ins>+from txdav.common.datastore.sql import CommonStoreTransaction
+from txdav.who.delegates import Delegates, RecordType as DelegateRecordType
</ins><span class="cx"> from txdav.who.groups import GroupCacher
</span><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="lines">@@ -46,10 +44,10 @@
</span><span class="cx"> delegate2 = yield self.directory.recordWithUID(u"__cdaboo1__")
</span><span class="cx">
</span><span class="cx"> # Add 1 delegate
</span><del>- yield addDelegate(txn, delegator, delegate1, True)
- delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+ yield Delegates.addDelegate(txn, delegator, delegate1, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True))
</ins><span class="cx"> self.assertEquals([u"__sagen1__"], [d.uid for d in delegates])
</span><del>- delegators = (yield delegatedTo(txn, delegate1, True))
</del><ins>+ delegators = (yield Delegates.delegatedTo(txn, delegate1, True))
</ins><span class="cx"> self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators])
</span><span class="cx">
</span><span class="cx"> yield txn.commit() # So delegateService will see the changes
</span><span class="lines">@@ -97,27 +95,27 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Add another delegate
</span><del>- yield addDelegate(txn, delegator, delegate2, True)
- delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+ yield Delegates.addDelegate(txn, delegator, delegate2, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__", u"__cdaboo1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="cx"> )
</span><del>- delegators = (yield delegatedTo(txn, delegate2, True))
</del><ins>+ delegators = (yield Delegates.delegatedTo(txn, delegate2, True))
</ins><span class="cx"> self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators])
</span><span class="cx">
</span><span class="cx"> # Remove 1 delegate
</span><del>- yield removeDelegate(txn, delegator, delegate1, True)
- delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+ yield Delegates.removeDelegate(txn, delegator, delegate1, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True))
</ins><span class="cx"> self.assertEquals([u"__cdaboo1__"], [d.uid for d in delegates])
</span><del>- delegators = (yield delegatedTo(txn, delegate1, True))
</del><ins>+ delegators = (yield Delegates.delegatedTo(txn, delegate1, True))
</ins><span class="cx"> self.assertEquals(0, len(delegators))
</span><span class="cx">
</span><span class="cx"> # Remove the other delegate
</span><del>- yield removeDelegate(txn, delegator, delegate2, True)
- delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+ yield Delegates.removeDelegate(txn, delegator, delegate2, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True))
</ins><span class="cx"> self.assertEquals(0, len(delegates))
</span><del>- delegators = (yield delegatedTo(txn, delegate2, True))
</del><ins>+ delegators = (yield Delegates.delegatedTo(txn, delegate2, True))
</ins><span class="cx"> self.assertEquals(0, len(delegators))
</span><span class="cx">
</span><span class="cx"> yield txn.commit() # So delegateService will see the changes
</span><span class="lines">@@ -131,7 +129,7 @@
</span><span class="cx">
</span><span class="cx"> # Verify the assignments were made
</span><span class="cx"> txn = self.store.newTransaction(label="test_directDelegation")
</span><del>- delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+ delegates = (yield Delegates.delegatesOf(txn, delegator, True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__", u"__cdaboo1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="lines">@@ -143,7 +141,7 @@
</span><span class="cx">
</span><span class="cx"> # Verify the assignments were made
</span><span class="cx"> txn = self.store.newTransaction(label="test_directDelegation")
</span><del>- delegates = (yield delegatesOf(txn, delegator, True))
</del><ins>+ delegates = (yield Delegates.delegatesOf(txn, delegator, True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__cdaboo1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="lines">@@ -161,31 +159,31 @@
</span><span class="cx"> group2 = yield self.directory.recordWithUID(u"__sub_group_1__")
</span><span class="cx">
</span><span class="cx"> # Add group delegate
</span><del>- yield addDelegate(txn, delegator, group1, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegator, group1, True)
</ins><span class="cx"> # Passing expanded=False will return the group
</span><del>- delegates = (yield delegatesOf(txn, delegator, True, expanded=False))
</del><ins>+ delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=False))
</ins><span class="cx"> self.assertEquals(1, len(delegates))
</span><span class="cx"> self.assertEquals(delegates[0].uid, u"__top_group_1__")
</span><span class="cx"> # Passing expanded=True will return not the group -- it only returns
</span><span class="cx"> # non-groups
</span><del>- delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</del><ins>+ delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="cx"> )
</span><del>- delegators = (yield delegatedTo(txn, delegate1, True))
</del><ins>+ delegators = (yield Delegates.delegatedTo(txn, delegate1, True))
</ins><span class="cx"> self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators])
</span><span class="cx">
</span><span class="cx"> # Verify we can ask for all delegated-to groups
</span><del>- yield addDelegate(txn, delegator, group2, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegator, group2, True)
</ins><span class="cx"> groups = (yield txn.allGroupDelegates())
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u'__sub_group_1__', u'__top_group_1__']), set(groups)
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Delegate to a user who is already indirectly delegated-to
</span><del>- yield addDelegate(txn, delegator, delegate1, True)
- delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</del><ins>+ yield Delegates.addDelegate(txn, delegator, delegate1, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="lines">@@ -205,23 +203,23 @@
</span><span class="cx"> _ignore_numAdded, _ignore_numRemoved = (
</span><span class="cx"> yield self.groupCacher.synchronizeMembers(txn, groupID, newSet)
</span><span class="cx"> )
</span><del>- delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</del><ins>+ delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__", u"__dre1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Remove delegate access from the top group
</span><del>- yield removeDelegate(txn, delegator, group1, True)
- delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</del><ins>+ yield Delegates.removeDelegate(txn, delegator, group1, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__", u"__cdaboo1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Remove delegate access from the sub group
</span><del>- yield removeDelegate(txn, delegator, group2, True)
- delegates = (yield delegatesOf(txn, delegator, True, expanded=True))
</del><ins>+ yield Delegates.removeDelegate(txn, delegator, group2, True)
+ delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u"__sagen1__"]),
</span><span class="cx"> set([d.uid for d in delegates])
</span><span class="lines">@@ -240,11 +238,11 @@
</span><span class="cx"> delegate1 = yield self.directory.recordWithUID(u"__sagen1__")
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><del>- yield addDelegate(txn, delegator, delegate1, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegator, delegate1, True)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><del>- yield addDelegate(txn, delegator, delegate1, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegator, delegate1, True)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><span class="lines">@@ -262,11 +260,11 @@
</span><span class="cx"> group1 = yield self.directory.recordWithUID(u"__top_group_1__")
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><del>- yield addDelegate(txn, delegator, group1, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegator, group1, True)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><del>- yield addDelegate(txn, delegator, group1, True)
</del><ins>+ yield Delegates.addDelegate(txn, delegator, group1, True)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><span class="lines">@@ -279,3 +277,572 @@
</span><span class="cx"> )
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> self.assertEquals([["__top_group_1__"]], results)
</span><ins>+
+
+
+class DelegationCachingTest(StoreTestCase):
+
+ @inlineCallbacks
+ def setUp(self):
+ yield super(DelegationCachingTest, self).setUp()
+ self.store = self.storeUnderTest()
+ self.groupCacher = GroupCacher(self.directory)
+
+
+ @inlineCallbacks
+ def _memcacherMemberResults(self, delegate, readWrite, expanded, results):
+ delegateUIDs = yield Delegates._memcacher.getMembers(delegate.uid, readWrite, expanded)
+ self.assertEqual(
+ set(delegateUIDs) if delegateUIDs is not None else None,
+ set([delegate.uid for delegate in results]) if results is not None else None,
+ msg="uid:{}, rw={}, expanded={}".format(delegate.uid, readWrite, expanded)
+ )
+
+
+ @inlineCallbacks
+ def _memcacherAllMemberResults(self, delegate, results1, results2, results3, results4):
+ for readWrite, expanded, results in (
+ (True, False, results1),
+ (True, True, results2),
+ (False, False, results3),
+ (False, True, results4),
+ ):
+ yield self._memcacherMemberResults(delegate, readWrite, expanded, results)
+
+
+ @inlineCallbacks
+ def _memcacherMembershipResults(self, delegate, readWrite, results):
+ delegatorUIDs = yield Delegates._memcacher.getMemberships(delegate.uid, readWrite)
+ self.assertEqual(
+ set(delegatorUIDs) if delegatorUIDs is not None else None,
+ set([delegator.uid for delegator in results]) if results is not None else None,
+ msg="uid:{}, rw={}".format(delegate.uid, readWrite)
+ )
+
+
+ @inlineCallbacks
+ def _memcacherAllMembershipResults(self, delegate, results1, results2):
+ for readWrite, results in (
+ (True, results1),
+ (False, results2),
+ ):
+ yield self._memcacherMembershipResults(delegate, readWrite, results)
+
+
+ @inlineCallbacks
+ def _delegatesOfResults(self, delegator, readWrite, expanded, results):
+ delegates = (yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, readWrite, expanded))
+ self.assertEquals(
+ set([d.uid for d in delegates]),
+ set([delegate.uid for delegate in results]),
+ msg="uid:{}, rw={}, expanded={}".format(delegator.uid, readWrite, expanded)
+ )
+
+
+ @inlineCallbacks
+ def _delegatesOfAllResults(self, delegator, results1, results2, results3, results4):
+ for readWrite, expanded, results in (
+ (True, False, results1),
+ (True, True, results2),
+ (False, False, results3),
+ (False, True, results4),
+ ):
+ yield self._delegatesOfResults(delegator, readWrite, expanded, results)
+
+
+ @inlineCallbacks
+ def _delegatedToResults(self, delegate, readWrite, results):
+ delegators = (yield Delegates.delegatedTo(self.transactionUnderTest(), delegate, readWrite))
+ self.assertEquals(
+ set([d.uid for d in delegators]),
+ set([delegator.uid for delegator in results]),
+ msg="uid:{}, rw={}".format(delegate.uid, readWrite)
+ )
+
+
+ @inlineCallbacks
+ def _delegatedToAllResults(self, delegator, results1, results2):
+ for readWrite, results in (
+ (True, results1),
+ (False, results2),
+ ):
+ yield self._delegatedToResults(delegator, readWrite, results)
+
+
+ @inlineCallbacks
+ def test_cacheUsed(self):
+
+ yield Delegates._memcacher.flushAll()
+
+ delegator = yield self.directory.recordWithUID(u"__wsanchez1__")
+ delegate1 = yield self.directory.recordWithUID(u"__sagen1__")
+
+ # Patch transaction so we can monitor whether cache is being used
+ original_delegates = CommonStoreTransaction.delegates
+ delegates_query = [0]
+ def _delegates(self, delegator, readWrite, expanded=False):
+ delegates_query[0] += 1
+ return original_delegates(self, delegator, readWrite, expanded)
+ self.patch(CommonStoreTransaction, "delegates", _delegates)
+
+ original_delegators = CommonStoreTransaction.delegators
+ delegators_query = [0]
+ def _delegators(self, delegate, readWrite):
+ delegators_query[0] += 1
+ return original_delegators(self, delegate, readWrite)
+ self.patch(CommonStoreTransaction, "delegators", _delegators)
+
+ # Not used
+ yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, True, False)
+ self.assertEqual(delegates_query[0], 1)
+
+ # Used
+ yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, True, False)
+ self.assertEqual(delegates_query[0], 1)
+
+ # Not used
+ yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False, False)
+ self.assertEqual(delegates_query[0], 2)
+
+ # Used
+ yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False, False)
+ self.assertEqual(delegates_query[0], 2)
+
+ # Not used
+ yield Delegates.delegatedTo(self.transactionUnderTest(), delegate1, True)
+ self.assertEqual(delegators_query[0], 1)
+
+ # Used
+ yield Delegates.delegatedTo(self.transactionUnderTest(), delegate1, True)
+ self.assertEqual(delegators_query[0], 1)
+
+ # Not used
+ yield Delegates.delegatedTo(self.transactionUnderTest(), delegate1, False)
+ self.assertEqual(delegators_query[0], 2)
+
+ # Used
+ yield Delegates.delegatedTo(self.transactionUnderTest(), delegate1, False)
+ self.assertEqual(delegators_query[0], 2)
+
+
+ @inlineCallbacks
+ def test_addRemoveDelegation(self):
+
+ yield Delegates._memcacher.flushAll()
+
+ delegator = yield self.directory.recordWithUID(u"__wsanchez1__")
+ delegate1 = yield self.directory.recordWithUID(u"__sagen1__")
+ delegate2 = yield self.directory.recordWithUID(u"__cdaboo1__")
+
+ # Add delegate
+ yield Delegates.addDelegate(self.transactionUnderTest(), delegator, delegate1, True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, [delegate1], None, None)
+ yield self._memcacherAllMemberResults(delegate1, None, None, None, None)
+ yield self._memcacherAllMemberResults(delegate2, None, None, None, None)
+ yield self._memcacherAllMembershipResults(delegator, None, None)
+ yield self._memcacherAllMembershipResults(delegate1, None, None)
+ yield self._memcacherAllMembershipResults(delegate2, None, None)
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(
+ delegator,
+ [delegate1], [delegate1], [], [],
+ )
+
+ yield self._delegatesOfAllResults(
+ delegate1,
+ [], [], [], [],
+ )
+
+ yield self._delegatesOfAllResults(
+ delegate2,
+ [], [], [], [],
+ )
+
+ yield self._delegatedToAllResults(
+ delegator,
+ [], [],
+ )
+
+ yield self._delegatedToAllResults(
+ delegate1,
+ [delegator], [],
+ )
+
+ yield self._delegatedToAllResults(
+ delegate2,
+ [], [],
+ )
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [delegate1], [delegate1], [], [])
+ yield self._memcacherAllMemberResults(delegate1, [], [], [], [])
+ yield self._memcacherAllMemberResults(delegate2, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegate1, [delegator], [])
+ yield self._memcacherAllMembershipResults(delegate2, [], [])
+
+ # Remove delegate
+ yield Delegates.removeDelegate(self.transactionUnderTest(), delegator, delegate1, True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, [], [], [])
+ yield self._memcacherAllMemberResults(delegate1, [], [], [], [])
+ yield self._memcacherAllMemberResults(delegate2, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegate1, None, [])
+ yield self._memcacherAllMembershipResults(delegate2, [], [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(
+ delegator,
+ [], [], [], [],
+ )
+
+ yield self._delegatesOfAllResults(
+ delegate1,
+ [], [], [], [],
+ )
+
+ yield self._delegatesOfAllResults(
+ delegate2,
+ [], [], [], [],
+ )
+
+ yield self._delegatedToAllResults(
+ delegator,
+ [], [],
+ )
+
+ yield self._delegatedToAllResults(
+ delegate1,
+ [], [],
+ )
+
+ yield self._delegatedToAllResults(
+ delegate2,
+ [], [],
+ )
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [], [], [], [])
+ yield self._memcacherAllMemberResults(delegate1, [], [], [], [])
+ yield self._memcacherAllMemberResults(delegate2, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegate1, [], [])
+ yield self._memcacherAllMembershipResults(delegate2, [], [])
+
+
+ @inlineCallbacks
+ def test_setDelegation(self):
+
+ yield Delegates._memcacher.flushAll()
+
+ delegator = yield self.directory.recordWithUID(u"__wsanchez1__")
+ delegates = [
+ (yield self.directory.recordWithUID(u"__sagen1__")),
+ (yield self.directory.recordWithUID(u"__cdaboo1__")),
+ (yield self.directory.recordWithUID(u"__dre1__")),
+ ]
+
+ # Add delegates
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [delegates[0], delegates[1]], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, [delegates[0], delegates[1]], None, None)
+ yield self._memcacherAllMembershipResults(delegator, None, None)
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, None, None, None, None)
+ yield self._memcacherAllMembershipResults(delegate, None, None)
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [delegates[0], delegates[1]], [delegates[0], delegates[1]], [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [delegator], [])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [delegates[0], delegates[1]], [delegates[0], delegates[1]], [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [], [])
+
+ # Remove delegate
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [delegates[1], delegates[2]], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, [delegates[1], delegates[2]], [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], None, [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], None, [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [delegates[1], delegates[2]], [delegates[1], delegates[2]], [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [], [])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [delegator], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [delegates[1], delegates[2]], [delegates[1], delegates[2]], [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [delegator], [])
+
+ # Add delegate with other mode
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [delegates[0]], False)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, [delegates[1], delegates[2]], [delegates[1], delegates[2]], None, [delegates[0]])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [], None)
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [delegator], [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [delegates[1], delegates[2]], [delegates[1], delegates[2]], [delegates[0]], [delegates[0]])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [], [delegator])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [delegator], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [delegates[1], delegates[2]], [delegates[1], delegates[2]], [delegates[0]], [delegates[0]])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [], [delegator])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [delegator], [])
+
+
+ @inlineCallbacks
+ def test_setGroupDelegation(self):
+
+ yield Delegates._memcacher.flushAll()
+
+ delegator = yield self.directory.recordWithUID(u"__wsanchez1__")
+ delegates = [
+ (yield self.directory.recordWithUID(u"__sagen1__")),
+ (yield self.directory.recordWithUID(u"__cdaboo1__")),
+ (yield self.directory.recordWithUID(u"__glyph1__")),
+ (yield self.directory.recordWithUID(u"__dre1__")),
+ ]
+ group1 = yield self.directory.recordWithUID(u"__top_group_1__")
+ group2 = yield self.directory.recordWithUID(u"__sub_group_1__")
+ yield self.transactionUnderTest().groupByUID(u"__top_group_1__")
+ yield self.transactionUnderTest().groupByUID(u"__sub_group_1__")
+ yield self.commit()
+
+ def delegateMatch(*args):
+ return [delegates[i] for i in args]
+
+ # Add group delegate
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [group1], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, delegateMatch(0, 1, 2), None, None)
+ yield self._memcacherAllMembershipResults(delegator, None, None)
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, None, None, None, None)
+ yield self._memcacherAllMembershipResults(delegate, None, None)
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [group1], delegateMatch(0, 1, 2), [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [delegator], [])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [delegator], [])
+ yield self._delegatedToAllResults(delegates[3], [], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [group1], delegateMatch(0, 1, 2), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [], [])
+
+ # Add individual delegate
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [group1, delegates[3]], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, delegateMatch(0, 1, 2, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[3], None, [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [group1, delegates[3]], delegateMatch(0, 1, 2, 3), [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [delegator], [])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [delegator], [])
+ yield self._delegatedToAllResults(delegates[3], [delegator], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [group1, delegates[3]], delegateMatch(0, 1, 2, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
+
+ # Switch to sub-group
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [group2, delegates[3]], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, delegateMatch(0, 1, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], None, [])
+ yield self._memcacherAllMembershipResults(delegates[1], None, [])
+ yield self._memcacherAllMembershipResults(delegates[2], None, [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [group2, delegates[3]], delegateMatch(0, 1, 3), [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [delegator], [])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [], [])
+ yield self._delegatedToAllResults(delegates[3], [delegator], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [group2, delegates[3]], delegateMatch(0, 1, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
+
+ # Add member of existing group
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [group2, delegates[0], delegates[3]], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, delegateMatch(0, 1, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [group2, delegates[0], delegates[3]], delegateMatch(0, 1, 3), [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [delegator], [])
+ yield self._delegatedToAllResults(delegates[1], [delegator], [])
+ yield self._delegatedToAllResults(delegates[2], [], [])
+ yield self._delegatedToAllResults(delegates[3], [delegator], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [group2, delegates[0], delegates[3]], delegateMatch(0, 1, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
+
+ # Remove group
+ yield Delegates.setDelegates(self.transactionUnderTest(), delegator, [delegates[0], delegates[3]], True)
+ yield self.commit()
+
+ # Some cache entries invalid
+ yield self._memcacherAllMemberResults(delegator, None, delegateMatch(0, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], None, [])
+ yield self._memcacherAllMembershipResults(delegates[2], [], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
+
+ # Read the delegate information twice - first time should be without cache, second with
+ for _ignore in range(2):
+ yield self._delegatesOfAllResults(delegator, [delegates[0], delegates[3]], delegateMatch(0, 3), [], [])
+ for delegate in delegates:
+ yield self._delegatesOfAllResults(delegate, [], [], [], [])
+
+ yield self._delegatedToAllResults(delegator, [], [])
+ yield self._delegatedToAllResults(delegates[0], [delegator], [])
+ yield self._delegatedToAllResults(delegates[1], [], [])
+ yield self._delegatedToAllResults(delegates[2], [], [])
+ yield self._delegatedToAllResults(delegates[3], [delegator], [])
+
+ # Check cache
+ yield self._memcacherAllMemberResults(delegator, [delegates[0], delegates[3]], delegateMatch(0, 3), [], [])
+ for delegate in delegates:
+ yield self._memcacherAllMemberResults(delegate, [], [], [], [])
+ yield self._memcacherAllMembershipResults(delegator, [], [])
+ yield self._memcacherAllMembershipResults(delegates[0], [delegator], [])
+ yield self._memcacherAllMembershipResults(delegates[1], [], [])
+ yield self._memcacherAllMembershipResults(delegates[2], [], [])
+ yield self._memcacherAllMembershipResults(delegates[3], [delegator], [])
</ins></span></pre></div>
<a id="CalendarServertrunktxdavwhotesttest_groupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/test/test_groups.py (14102 => 14103)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/test/test_groups.py        2014-10-24 17:34:33 UTC (rev 14102)
+++ CalendarServer/trunk/txdav/who/test/test_groups.py        2014-10-24 21:12:40 UTC (rev 14103)
</span><span class="lines">@@ -277,7 +277,6 @@
</span><span class="cx"> delegates,
</span><span class="cx"> set(
</span><span class="cx"> [
</span><del>- u"__wsanchez1__",
</del><span class="cx"> u"__sagen1__",
</span><span class="cx"> u"__cdaboo1__",
</span><span class="cx"> u"__glyph1__"
</span></span></pre>
</div>
</div>
</body>
</html>