s4-repl: Allow dsdb_replicated_objects_commit() to use different schema while committ...
authorKamen Mazdrashki <kamenim@samba.org>
Fri, 10 Dec 2010 00:55:30 +0000 (02:55 +0200)
committerKamen Mazdrashki <kamenim@samba.org>
Tue, 14 Dec 2010 23:51:19 +0000 (00:51 +0100)
working_schema is to be used while committing a Schema replica.

When we replicate Schema, then we most probably won't be
able to convert all replicated objects using the current
Schema cache (as we don't know anything about those new objects).

Thus, during Schema replication, we make a temporary
working_schema that contains both our current Schema +
all objects we get on the wire.
When we commit those new objects, we should use our working_schema
(by setting it to the ldb), and after all changes are commited,
we can refresh the schema cache so we have a brand new,
full-featured Schema cache

source4/dsdb/repl/drepl_out_helpers.c
source4/dsdb/repl/replicated_objects.c
source4/libnet/libnet_vampire.c

index f02fae951017bc8fb39a10ec6640a09c9b6ef39e..8c5c9da6c3e16fe71708d8987171fdcda2ed2b75 100644 (file)
@@ -629,6 +629,7 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
        }
 
        status = dsdb_replicated_objects_commit(service->samdb,
        }
 
        status = dsdb_replicated_objects_commit(service->samdb,
+                                               NULL,
                                                objects,
                                                &state->op->source_dsa->notify_uSN);
        talloc_free(objects);
                                                objects,
                                                &state->op->source_dsa->notify_uSN);
        talloc_free(objects);
@@ -639,6 +640,7 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req
                tevent_req_nterror(req, nt_status);
                return;
        }
                tevent_req_nterror(req, nt_status);
                return;
        }
+
        if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
                /* if it applied fine, we need to update the highwatermark */
                *state->op->source_dsa->repsFrom1 = rf1;
        if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
                /* if it applied fine, we need to update the highwatermark */
                *state->op->source_dsa->repsFrom1 = rf1;
index 1ea1640e2958f8f69d00ac79306df45e03b6fa10..f3b6356649b8fca0eee336f54ba8a62079f01dc7 100644 (file)
@@ -433,11 +433,20 @@ WERROR dsdb_replicated_objects_convert(struct ldb_context *ldb,
        return WERR_OK;
 }
 
        return WERR_OK;
 }
 
+/**
+ * Commits a list of replicated objects.
+ *
+ * @param working_schema dsdb_schema to be used for resolving
+ *                      Classes/Attributes during Schema replication. If not NULL,
+ *                      it will be set on ldb and used while committing replicated objects
+ */
 WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
 WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
+                                     struct dsdb_schema *working_schema,
                                      struct dsdb_extended_replicated_objects *objects,
                                      uint64_t *notify_uSN)
 {
        struct ldb_result *ext_res;
                                      struct dsdb_extended_replicated_objects *objects,
                                      uint64_t *notify_uSN)
 {
        struct ldb_result *ext_res;
+       struct dsdb_schema *cur_schema = NULL;
        int ret;
        uint64_t seq_num1, seq_num2;
 
        int ret;
        uint64_t seq_num1, seq_num2;
 
@@ -459,8 +468,33 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
                return WERR_FOOBAR;             
        }
 
                return WERR_FOOBAR;             
        }
 
+       /*
+        * Set working_schema for ldb in case we are replicating from Schema NC.
+        * Schema won't be reloaded during Replicated Objects commit, as it is
+        * done in a transaction. So we need some way to search for newly
+        * added Classes and Attributes
+        */
+       if (working_schema) {
+               /* store current schema so we can fall back in case of failure */
+               cur_schema = dsdb_get_schema(ldb, objects);
+
+               ret = dsdb_reference_schema(ldb, working_schema, false);
+               if (ret != LDB_SUCCESS) {
+                       DEBUG(0,(__location__ "Failed to reference working schema - %s\n",
+                                ldb_strerror(ret)));
+                       /* TODO: Map LDB Error to NTSTATUS? */
+                       ldb_transaction_cancel(ldb);
+                       return WERR_INTERNAL_ERROR;
+               }
+       }
+
        ret = ldb_extended(ldb, DSDB_EXTENDED_REPLICATED_OBJECTS_OID, objects, &ext_res);
        if (ret != LDB_SUCCESS) {
        ret = ldb_extended(ldb, DSDB_EXTENDED_REPLICATED_OBJECTS_OID, objects, &ext_res);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               if (cur_schema ) {
+                       dsdb_reference_schema(ldb, cur_schema, false);
+               }
+
                DEBUG(0,("Failed to apply records: %s: %s\n",
                         ldb_errstring(ldb), ldb_strerror(ret)));
                ldb_transaction_cancel(ldb);
                DEBUG(0,("Failed to apply records: %s: %s\n",
                         ldb_errstring(ldb), ldb_strerror(ret)));
                ldb_transaction_cancel(ldb);
@@ -470,6 +504,10 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
 
        ret = ldb_transaction_prepare_commit(ldb);
        if (ret != LDB_SUCCESS) {
 
        ret = ldb_transaction_prepare_commit(ldb);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               if (cur_schema ) {
+                       dsdb_reference_schema(ldb, cur_schema, false);
+               }
                DEBUG(0,(__location__ " Failed to prepare commit of transaction: %s\n",
                         ldb_errstring(ldb)));
                return WERR_FOOBAR;
                DEBUG(0,(__location__ " Failed to prepare commit of transaction: %s\n",
                         ldb_errstring(ldb)));
                return WERR_FOOBAR;
@@ -477,6 +515,10 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
 
        ret = dsdb_load_partition_usn(ldb, objects->partition_dn, &seq_num2, NULL);
        if (ret != LDB_SUCCESS) {
 
        ret = dsdb_load_partition_usn(ldb, objects->partition_dn, &seq_num2, NULL);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               if (cur_schema ) {
+                       dsdb_reference_schema(ldb, cur_schema, false);
+               }
                DEBUG(0,(__location__ " Failed to load partition uSN\n"));
                ldb_transaction_cancel(ldb);
                return WERR_FOOBAR;             
                DEBUG(0,(__location__ " Failed to load partition uSN\n"));
                ldb_transaction_cancel(ldb);
                return WERR_FOOBAR;             
@@ -491,10 +533,23 @@ WERROR dsdb_replicated_objects_commit(struct ldb_context *ldb,
 
        ret = ldb_transaction_commit(ldb);
        if (ret != LDB_SUCCESS) {
 
        ret = ldb_transaction_commit(ldb);
        if (ret != LDB_SUCCESS) {
+               /* restore previous schema */
+               if (cur_schema ) {
+                       dsdb_reference_schema(ldb, cur_schema, false);
+               }
                DEBUG(0,(__location__ " Failed to commit transaction\n"));
                return WERR_FOOBAR;
        }
 
                DEBUG(0,(__location__ " Failed to commit transaction\n"));
                return WERR_FOOBAR;
        }
 
+       /*
+        * Reset the Schema used by ldb. This will lead to
+        * a schema cache being refreshed from database.
+        */
+       if (working_schema) {
+               cur_schema = dsdb_get_schema(ldb, NULL);
+               /* TODO: What we do in case dsdb_get_schema() fail?
+                *       We can't fallback at this point anymore */
+       }
 
        DEBUG(2,("Replicated %u objects (%u linked attributes) for %s\n",
                 objects->num_objects, objects->linked_attributes_count,
 
        DEBUG(2,("Replicated %u objects (%u linked attributes) for %s\n",
                 objects->num_objects, objects->linked_attributes_count,
index 1d7d7268aecaee69772691224839b33024d9daca..40cf01ed528740a4b5df08380fbf139bddd428e6 100644 (file)
@@ -446,7 +446,7 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
                }
        }
 
                }
        }
 
-       status = dsdb_replicated_objects_commit(s->ldb, schema_objs, &seq_num);
+       status = dsdb_replicated_objects_commit(s->ldb, NULL, schema_objs, &seq_num);
        if (!W_ERROR_IS_OK(status)) {
                DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
                return werror_to_ntstatus(status);
        if (!W_ERROR_IS_OK(status)) {
                DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
                return werror_to_ntstatus(status);
@@ -720,7 +720,7 @@ NTSTATUS libnet_vampire_cb_store_chunk(void *private_data,
                        NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
                }
        }
                        NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
                }
        }
-       status = dsdb_replicated_objects_commit(s->ldb, objs, &seq_num);
+       status = dsdb_replicated_objects_commit(s->ldb, NULL, objs, &seq_num);
        if (!W_ERROR_IS_OK(status)) {
                DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
                return werror_to_ntstatus(status);
        if (!W_ERROR_IS_OK(status)) {
                DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
                return werror_to_ntstatus(status);