tests:samba_tool: pass stdout and stderr to assertCmdSuccess()
[nivanova/samba-autobuild/.git] / source4 / torture / drs / python / repl_schema.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Tests various schema replication scenarios
5 #
6 # Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22 #
23 # Usage:
24 #  export DC1=dc1_dns_name
25 #  export DC2=dc2_dns_name
26 #  export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun
27 #  PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN repl_schema -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD"
28 #
29
30 import time
31 import random
32
33 from ldb import (
34     ERR_NO_SUCH_OBJECT,
35     LdbError,
36     SCOPE_BASE,
37     Message,
38     FLAG_MOD_ADD,
39     FLAG_MOD_REPLACE,
40     )
41 import ldb
42
43 import drs_base
44 from samba.dcerpc import drsuapi, misc
45 from samba.drs_utils import drs_DsBind
46
47 class DrsReplSchemaTestCase(drs_base.DrsBaseTestCase):
48
49     # prefix for all objects created
50     obj_prefix = None
51     # current Class or Attribute object id
52     obj_id = 0
53
54     def _ds_bind(self, server_name):
55         binding_str = "ncacn_ip_tcp:%s[seal]" % server_name
56
57         drs = drsuapi.drsuapi(binding_str, self.get_loadparm(), self.get_credentials())
58         (drs_handle, supported_extensions) = drs_DsBind(drs)
59         return (drs, drs_handle)
60
61     def _exop_req8(self, dest_dsa, invocation_id, nc_dn_str, exop,
62                    replica_flags=0, max_objects=0):
63         req8 = drsuapi.DsGetNCChangesRequest8()
64
65         req8.destination_dsa_guid = misc.GUID(dest_dsa) if dest_dsa else misc.GUID()
66         req8.source_dsa_invocation_id = misc.GUID(invocation_id)
67         req8.naming_context = drsuapi.DsReplicaObjectIdentifier()
68         req8.naming_context.dn = unicode(nc_dn_str)
69         req8.highwatermark = drsuapi.DsReplicaHighWaterMark()
70         req8.highwatermark.tmp_highest_usn = 0
71         req8.highwatermark.reserved_usn = 0
72         req8.highwatermark.highest_usn = 0
73         req8.uptodateness_vector = None
74         req8.replica_flags = replica_flags
75         req8.max_object_count = max_objects
76         req8.max_ndr_size = 402116
77         req8.extended_op = exop
78         req8.fsmo_info = 0
79         req8.partial_attribute_set = None
80         req8.partial_attribute_set_ex = None
81         req8.mapping_ctr.num_mappings = 0
82         req8.mapping_ctr.mappings = None
83
84         return req8
85
86     def setUp(self):
87         super(DrsReplSchemaTestCase, self).setUp()
88
89         # disable automatic replication temporary
90         self._disable_all_repl(self.dnsname_dc1)
91         self._disable_all_repl(self.dnsname_dc2)
92
93         # make sure DCs are synchronized before the test
94         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
95         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
96         # initialize objects prefix if not done yet
97         if self.obj_prefix is None:
98             t = time.strftime("%s", time.gmtime())
99             DrsReplSchemaTestCase.obj_prefix = "DrsReplSchema-%s" % t
100
101     def tearDown(self):
102         self._enable_all_repl(self.dnsname_dc1)
103         self._enable_all_repl(self.dnsname_dc2)
104         super(DrsReplSchemaTestCase, self).tearDown()
105
106     def _make_obj_names(self, base_name):
107         '''Try to create a unique name for an object
108            that is to be added to schema'''
109         self.obj_id += 1
110         obj_name = "%s-%d-%s" % (self.obj_prefix, self.obj_id, base_name)
111         obj_ldn = obj_name.replace("-", "")
112         obj_dn = ldb.Dn(self.ldb_dc1, "CN=X")
113         obj_dn.add_base(ldb.Dn(self.ldb_dc1, self.schema_dn))
114         obj_dn.set_component(0, "CN", obj_name)
115         return (obj_dn, obj_name, obj_ldn)
116
117     def _schema_new_class(self, ldb_ctx, base_name, base_int, oc_cat=1, attrs=None):
118         (class_dn, class_name, class_ldn) = self._make_obj_names(base_name)
119         rec = {"dn": class_dn,
120                "objectClass": ["top", "classSchema"],
121                "cn": class_name,
122                "lDAPDisplayName": class_ldn,
123                "governsId": "1.3.6.1.4.1.7165.4.6.2." \
124                 + str((100000 * base_int) + random.randint(1,100000)) + ".1.5.13",
125                "instanceType": "4",
126                "objectClassCategory": "%d" % oc_cat,
127                "subClassOf": "top",
128                "systemOnly": "FALSE"}
129         # allow overriding/adding attributes
130         if not attrs is None:
131             rec.update(attrs)
132         # add it to the Schema
133         try:
134             ldb_ctx.add(rec)
135         except LdbError, (enum, estr):
136             self.fail("Adding record failed with %d/%s" % (enum, estr))
137
138         self._ldap_schemaUpdateNow(ldb_ctx)
139         return (rec["lDAPDisplayName"], rec["dn"])
140
141     def _schema_new_attr(self, ldb_ctx, base_name, base_int, attrs=None):
142         (attr_dn, attr_name, attr_ldn) = self._make_obj_names(base_name)
143         rec = {"dn": attr_dn,
144                "objectClass": ["top", "attributeSchema"],
145                "cn": attr_name,
146                "lDAPDisplayName": attr_ldn,
147                "attributeId": "1.3.6.1.4.1.7165.4.6.1." \
148                 + str((100000 * base_int) + random.randint(1,100000)) + ".1.5.13",
149                "attributeSyntax": "2.5.5.12",
150                "omSyntax": "64",
151                "instanceType": "4",
152                "isSingleValued": "TRUE",
153                "systemOnly": "FALSE"}
154         # allow overriding/adding attributes
155         if not attrs is None:
156             rec.update(attrs)
157         # add it to the Schema
158         ldb_ctx.add(rec)
159         self._ldap_schemaUpdateNow(ldb_ctx)
160         return (rec["lDAPDisplayName"], rec["dn"])
161
162     def _check_object(self, obj_dn):
163         '''Check if object obj_dn exists on both DCs'''
164         res_dc1 = self.ldb_dc1.search(base=obj_dn,
165                                       scope=SCOPE_BASE,
166                                       attrs=["*"])
167         self.assertEquals(len(res_dc1), 1,
168                           "%s doesn't exists on %s" % (obj_dn, self.dnsname_dc1))
169         try:
170             res_dc2 = self.ldb_dc2.search(base=obj_dn,
171                                           scope=SCOPE_BASE,
172                                           attrs=["*"])
173         except LdbError, (enum, estr):
174             if enum == ERR_NO_SUCH_OBJECT:
175                 self.fail("%s doesn't exists on %s" % (obj_dn, self.dnsname_dc2))
176             raise
177         self.assertEquals(len(res_dc2), 1,
178                           "%s doesn't exists on %s" % (obj_dn, self.dnsname_dc2))
179
180     def test_class(self):
181         """Simple test for classSchema replication"""
182         # add new classSchema object
183         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-S", 0)
184         # force replication from DC1 to DC2
185         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True)
186         # check object is replicated
187         self._check_object(c_dn)
188
189     def test_classInheritance(self):
190         """Test inheritance through subClassOf
191            I think 5 levels of inheritance is pretty decent for now."""
192         # add 5 levels deep hierarchy
193         c_dn_list = []
194         c_ldn_last = None
195         for i in range(1, 6):
196             base_name = "cls-I-%02d" % i
197             (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, base_name, i)
198             c_dn_list.append(c_dn)
199             if c_ldn_last:
200                 # inherit from last class added
201                 m = Message.from_dict(self.ldb_dc1,
202                                       {"dn": c_dn,
203                                        "subClassOf": c_ldn_last},
204                                       FLAG_MOD_REPLACE)
205                 self.ldb_dc1.modify(m)
206             # store last class ldapDisplayName
207             c_ldn_last = c_ldn
208         # force replication from DC1 to DC2
209         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True)
210         # check objects are replicated
211         for c_dn in c_dn_list:
212             self._check_object(c_dn)
213
214     def test_classWithCustomAttribute(self):
215         """Create new Attribute and a Class,
216            that has value for newly created attribute.
217            This should check code path that searches for
218            AttributeID_id in Schema cache"""
219         # add new attributeSchema object
220         (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-A", 1)
221         # add a base classSchema class so we can use our new
222         # attribute in class definition in a sibling class
223         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-A", 7,
224                                                1,
225                                                {"systemMayContain": a_ldn,
226                                                 "subClassOf": "classSchema"})
227         # add new classSchema object with value for a_ldb attribute
228         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-B", 8,
229                                                1,
230                                                {"objectClass": ["top", "classSchema", c_ldn],
231                                                 a_ldn: "test_classWithCustomAttribute"})
232         # force replication from DC1 to DC2
233         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True)
234         # check objects are replicated
235         self._check_object(c_dn)
236         self._check_object(a_dn)
237
238     def test_classWithCustomLinkAttribute(self):
239         """Create new Attribute and a Class,
240            that has value for newly created attribute.
241            This should check code path that searches for
242            AttributeID_id in Schema cache"""
243         # add new attributeSchema object
244         (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-Link-X", 1,
245                                               attrs={'linkID':"99990",
246                                                      "attributeSyntax": "2.5.5.1",
247                                                      "omSyntax": "127"})
248         # add a base classSchema class so we can use our new
249         # attribute in class definition in a sibling class
250         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-Link-Y", 7,
251                                                1,
252                                                {"systemMayContain": a_ldn,
253                                                 "subClassOf": "classSchema"})
254         # add new classSchema object with value for a_ldb attribute
255         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-Link-Z", 8,
256                                                1,
257                                                {"objectClass": ["top", "classSchema", c_ldn],
258                                                 a_ldn: self.schema_dn})
259         # force replication from DC1 to DC2
260         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True)
261         # check objects are replicated
262         self._check_object(c_dn)
263         self._check_object(a_dn)
264
265         res = self.ldb_dc1.search(base=a_dn,
266                                   scope=SCOPE_BASE,
267                                   attrs=["msDS-IntId"])
268         self.assertEqual(1, len(res))
269         self.assertTrue("msDS-IntId" in res[0])
270         int_id = int(res[0]["msDS-IntId"][0])
271         if int_id < 0:
272             int_id += (1 << 32)
273
274         dc_guid_1 = self.ldb_dc1.get_invocation_id()
275
276         drs, drs_handle = self._ds_bind(self.dnsname_dc1)
277
278         req8 = self._exop_req8(dest_dsa=None,
279                 invocation_id=dc_guid_1,
280                 nc_dn_str=c_dn,
281                 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
282                 replica_flags=drsuapi.DRSUAPI_DRS_SYNC_FORCED)
283
284         (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
285
286         for link in ctr.linked_attributes:
287             self.assertTrue(link.attid != int_id,
288                             'Got %d for both' % link.attid)
289
290     def test_attribute(self):
291         """Simple test for attributeSchema replication"""
292         # add new attributeSchema object
293         (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-S", 2)
294         # force replication from DC1 to DC2
295         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True)
296         # check object is replicated
297         self._check_object(a_dn)
298
299     def test_attribute_on_ou(self):
300         """Simple test having an OU with a custome attribute replicated correctly
301
302         This ensures that the server
303         """
304         # disable automatic replication temporary
305         self._disable_all_repl(self.dnsname_dc1)
306         self._disable_all_repl(self.dnsname_dc2)
307
308        # add new attributeSchema object
309         (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-OU-S", 3)
310         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-OU-A", 8,
311                                                3,
312                                                {"mayContain": a_ldn})
313         ou_dn = ldb.Dn(self.ldb_dc1, "ou=X")
314         ou_dn.add_base(self.ldb_dc1.get_default_basedn())
315         ou_dn.set_component(0, "OU", a_dn.get_component_value(0))
316         rec = {"dn": ou_dn,
317                "objectClass": ["top", "organizationalUnit", c_ldn],
318                "ou": ou_dn.get_component_value(0),
319                a_ldn: "test OU"}
320         self.ldb_dc1.add(rec)
321
322         # force replication from DC1 to DC2
323         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.domain_dn, forced=True)
324         # check objects are replicated
325         self._check_object(c_dn)
326         self._check_object(a_dn)
327         self._check_object(ou_dn)
328         self.ldb_dc1.delete(ou_dn)
329
330     def test_all(self):
331         """Basic plan is to create bunch of classSchema
332            and attributeSchema objects, replicate Schema NC
333            and then check all objects are replicated correctly"""
334
335         # add new classSchema object
336         (c_ldn, c_dn) = self._schema_new_class(self.ldb_dc1, "cls-A", 9)
337         # add new attributeSchema object
338         (a_ldn, a_dn) = self._schema_new_attr(self.ldb_dc1, "attr-A", 3)
339
340         # add attribute to the class we have
341         m = Message.from_dict(self.ldb_dc1,
342                               {"dn": c_dn,
343                                "mayContain": a_ldn},
344                               FLAG_MOD_ADD)
345         self.ldb_dc1.modify(m)
346
347         # force replication from DC1 to DC2
348         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, nc_dn=self.schema_dn, forced=True)
349         
350         # check objects are replicated
351         self._check_object(c_dn)
352         self._check_object(a_dn)