infocalypse

(djk)
2009-04-05: As is checkin of reinserting. CLEANUP.

As is checkin of reinserting. CLEANUP.

diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py
--- a/infocalypse/__init__.py
+++ b/infocalypse/__init__.py
@@ -121,7 +121,7 @@ import os
 from mercurial import commands, util
 
 from infcmds import get_config_info, execute_create, execute_pull, \
-     execute_push, execute_setup, execute_copy
+     execute_push, execute_setup, execute_copy, execute_reinsert
 
 def set_target_version(ui_, repo, opts, params, msg_fmt):
     """ INTERNAL: Update TARGET_VERSION in params. """
@@ -172,6 +172,32 @@ def infocalypse_copy(ui_, repo, **opts):
     params['REQUEST_URI'] = request_uri
     execute_copy(ui_, repo, params, stored_cfg)
 
+def infocalypse_reinsert(ui_, repo, **opts):
+    """ Re-insert Infocalypse repository data. """
+    params, stored_cfg = get_config_info(ui_, opts)
+
+    request_uri = opts['requesturi']
+    if request_uri == '':
+        request_uri = stored_cfg.get_request_uri(repo.root)
+        if not request_uri:
+            ui_.warn("There is no stored request URI for this repo.\n"
+                     "Please set one with the --requesturi option.\n")
+            return
+
+    insert_uri = opts['inserturi']
+    if insert_uri == '':
+        insert_uri = stored_cfg.get_dir_insert_uri(repo.root)
+        # REDFLAG: fix parameter definition so that it is required?
+        if not insert_uri:
+            ui_.status("No insert URI specified. Will skip re-insert "
+                       +"of top key.\n")
+            insert_uri = None
+
+    params['INSERT_URI'] = insert_uri
+    params['REQUEST_URI'] = request_uri
+    execute_reinsert(ui_, repo, params, stored_cfg)
+
+
 def infocalypse_pull(ui_, repo, **opts):
     """ Pull from an Infocalypse repository in Freenet.
      """
@@ -257,6 +283,12 @@ cmdtable = {
                 + FCP_OPTS,
                 "[options]"),
 
+    "fn-reinsert": (infocalypse_reinsert,
+                [('', 'requesturi', '', 'request URI to re-insert data from'),
+                 ('', 'inserturi', '', 'insert URI (required to re-insert the top key)'), ]
+                + FCP_OPTS,
+                "[options]"),
+
     "fn-setup": (infocalypse_setup,
                  [('', 'tmpdir', '~/infocalypse_tmp', 'temp directory'),]
                  + FCP_OPTS,
diff --git a/infocalypse/graph.py b/infocalypse/graph.py
--- a/infocalypse/graph.py
+++ b/infocalypse/graph.py
@@ -328,10 +328,16 @@ class UpdateGraph:
         if not edge_list[ordinal + 1].startswith(PENDING_INSERT):
             print "set_chk -- replacing a non pending chk (%i, %i, %i)?" % \
                   (index_pair[0], index_pair[1], ordinal)
+            if edge_list[ordinal + 1] == chk:
+                print "Values are same."
+            else:
+                print "Values are different:"
+                print "old:", edge_list[ordinal + 1]
+                print "new:", chk
         edge_list[ordinal + 1] = chk
         self.edge_table[index_pair] = tuple(edge_list)
 
-    def insert_type(self, edge_triple):
+    def insert_type_(self, edge_triple):
         """ Return the kind of insert required to insert the CHK
             for the edge.
 
@@ -349,6 +355,34 @@ class UpdateGraph:
             return INSERT_PADDED
         return INSERT_SALTED_METADATA
 
+    def insert_type(self, edge_triple):
+        """ Return the kind of insert required to insert the CHK
+            for the edge.
+
+            INSERT_NORMAL -> No modification to the bundle file.
+            INSERT_PADDED -> Add one trailing pad byte.
+            INSERT_SALTED_METADATA -> Copy and salt the Freenet
+            split file metadata for the normal insert. """
+
+        if edge_triple[2] == 0:
+            return INSERT_NORMAL
+
+        assert edge_triple[2] == 1
+
+        length = self.edge_table[edge_triple[:2]][0]
+
+        # REDFLAG: DCI. MUST DEAL WITH ==32k case
+        if length <= FREENET_BLOCK_LEN:
+            # Made redundant path by padding.
+            return  INSERT_PADDED
+
+        if length <= MAX_METADATA_HACK_LEN:
+            return INSERT_SALTED_METADATA
+
+        print "insert_type called for edge that's too big to salt???"
+        print edge_triple
+        assert False
+
     def insert_length(self, step):
         """ Returns the actual length of the data inserted into
             Freenet for the edge. """
diff --git a/infocalypse/infcmds.py b/infocalypse/infcmds.py
--- a/infocalypse/infcmds.py
+++ b/infocalypse/infcmds.py
@@ -471,6 +471,38 @@ def execute_copy(ui_, repo, params, stor
     finally:
         cleanup(update_sm)
 
+def execute_reinsert(ui_, repo, params, stored_cfg):
+    """ Run the reinsert command. """
+    update_sm = None
+    try:
+        update_sm = setup(ui_, repo, params, stored_cfg)
+        request_uri, is_keypair = handle_key_inversion(ui_, update_sm,
+                                                       params, stored_cfg)
+        params['REQUEST_URI'] = request_uri
+
+        if not params['INSERT_URI'] is None:
+            ui_.status("%sInsert URI:\n%s\n" % (is_redundant(params['INSERT_URI']),
+                                                params['INSERT_URI']))
+        ui_.status("%sRequest URI:\n%s\n" % (is_redundant(params['REQUEST_URI']),
+                                             params['REQUEST_URI']))
+
+        update_sm.start_reinserting(params['REQUEST_URI'],
+                                    params['INSERT_URI'],
+                                    is_keypair)
+
+        run_until_quiescent(update_sm, params['POLL_SECS'])
+
+        if update_sm.get_state(QUIESCENT).arrived_from(((FINISHING,))):
+            ui_.status("Reinsert finished.\n")
+        else:
+            ui_.status("Reinsert failed.\n")
+
+        # Don't need to update the config.
+    finally:
+        cleanup(update_sm)
+
+
+
 # REDFLAG: move into fcpclient?
 #def usks_equal(usk_a, usk_b):
 #    assert is_usk(usk_a) and and is_usk(usk_b)
diff --git a/infocalypse/insertingbundles.py b/infocalypse/insertingbundles.py
--- a/infocalypse/insertingbundles.py
+++ b/infocalypse/insertingbundles.py
@@ -57,7 +57,8 @@ class InsertingBundles(RequestQueueState
 
         """
         #require_state(from_state, QUIESCENT)
-        assert not self.parent.ctx['INSERT_URI'] is None
+        assert (self.parent.ctx.get('REINSERT', 0) > 0 or
+                (not self.parent.ctx['INSERT_URI'] is None))
         assert not self.parent.ctx.graph is None
 
         graph = self.parent.ctx.graph.clone()
@@ -67,10 +68,7 @@ class InsertingBundles(RequestQueueState
 
         # Update graph.
         try:
-            self.new_edges = graph.update(self.parent.ctx.repo,
-                                          self.parent.ctx.ui_,
-                                          self.parent.ctx['TARGET_VERSION'],
-                                          self.parent.ctx.bundle_cache)
+            self.set_new_edges(graph)
         except UpToDate, err:
             # REDFLAG: Later, add FORCE_INSERT parameter?
             self.parent.ctx.ui_.warn(str(err) + '\n') # Hmmm
@@ -109,6 +107,7 @@ class InsertingBundles(RequestQueueState
         for edge in self.required_edges:
             # Will be re-added when the required metadata arrives.
             self.new_edges.remove((edge[0], edge[1], 1))
+        print "REQUIRED_EDGES:", self.required_edges, self.new_edges
 
     # REDFLAG: no longer needed?
     def leave(self, dummy):
@@ -130,7 +129,15 @@ class InsertingBundles(RequestQueueState
             if edge in self.pending:
                 # Already running.
                 continue
-            if not self.parent.ctx.graph.has_chk(edge):
+
+            # We can't count on the graph when reinserting.
+            # Because the chks are already set.
+
+            #if not self.parent.ctx.graph.has_chk(edge):
+            #    # Depends on an edge which hasn't been inserted yet.
+            #    continue
+
+            if edge in self.new_edges:
                 # Depends on an edge which hasn't been inserted yet.
                 continue
 
@@ -196,3 +203,21 @@ class InsertingBundles(RequestQueueState
             len(self.required_edges) == 0):
             self.parent.transition(INSERTING_GRAPH)
 
+    def set_new_edges(self, graph):
+        """ INTERNAL: Set the list of new edges to insert. """
+        if self.parent.ctx.get('REINSERT', 0) == 0:
+            self.new_edges = graph.update(self.parent.ctx.repo,
+                                          self.parent.ctx.ui_,
+                                          self.parent.ctx['TARGET_VERSION'],
+                                          self.parent.ctx.bundle_cache)
+            return
+
+        # Hmmmm... later support different int values of REINSERT?
+        self.new_edges = graph.get_top_key_edges()
+        redundant = []
+        for edge in  self.new_edges:
+            if graph.is_redundant(edge):
+                alternate_edge = (edge[0], edge[1], int(not edge[2]))
+                if not alternate_edge in self.new_edges:
+                    redundant.append(alternate_edge)
+        self.new_edges += redundant
diff --git a/infocalypse/updatesm.py b/infocalypse/updatesm.py
--- a/infocalypse/updatesm.py
+++ b/infocalypse/updatesm.py
@@ -87,16 +87,17 @@ def make_search_uris(uri):
     return (uri, '/'.join(fields))
 
 # For insert
-def make_insert_uris(uri):
+def make_insert_uris(uri, increment=True):
     """ Returns a possibly redundant insert uri tuple.
-        NOTE: This increments the version by 1 if uri is a USK.
+        NOTE: This increments the version by 1 if uri is a USK
+              and increment is True.
     """
     if uri == 'CHK@':
         return (uri,)
     assert is_usk_file(uri)
     version = get_version(uri)
     # REDFLAG: does index increment really belong here?
-    return make_redundant_ssk(uri, version + 1)
+    return make_redundant_ssk(uri, version + int(bool(increment)))
 
 def ssk_to_usk(ssk):
     """ Convert an SSK for a file USK back into a file USK. """
@@ -511,10 +512,19 @@ class InsertingUri(StaticRequestList):
         if not hasattr(from_state, 'get_top_key_tuple'):
             raise Exception("Illegal Transition from: %s" % from_state.name)
 
+        if (self.parent.ctx['INSERT_URI'] is None
+            and self.parent.ctx.get('REINSERT', 0) > 0):
+            # Hmmmm... hackery to deal with reinsert w/o insert uri
+            self.parent.transition(self.success_state)
+            return
+
+        assert not self.parent.ctx['INSERT_URI'] is None
+
         top_key_tuple = from_state.get_top_key_tuple()
 
         salt = {0:0x00, 1:0xff} # grrr.... less code.
-        insert_uris = make_insert_uris(self.parent.ctx['INSERT_URI'])
+        insert_uris = make_insert_uris(self.parent.ctx['INSERT_URI'],
+                                       self.parent.ctx.get('REINSERT', 0) < 1)
         assert len(insert_uris) < 3
         for index, uri in enumerate(insert_uris):
             if self.parent.params.get('DUMP_URIS', False):
@@ -529,7 +539,8 @@ class InsertingUri(StaticRequestList):
         if to_state.name == self.success_state:
             # Hmmm... what about chks?
             # Update the index in the insert_uri on success
-            if is_usk(self.parent.ctx['INSERT_URI']):
+            if (self.parent.ctx.get('REINSERT', 0) < 1 and
+                is_usk(self.parent.ctx['INSERT_URI'])):
                 version = get_version(self.parent.ctx['INSERT_URI']) + 1
                 self.parent.ctx['INSERT_URI'] = (
                     get_usk_for_usk_version(self.parent.ctx['INSERT_URI'],
@@ -955,6 +966,17 @@ class UpdateStateMachine(RequestQueue, S
         self.get_state(INVERTING_URI).insert_uri = insert_uri
         self.transition(INVERTING_URI)
 
+    def start_reinserting(self, request_uri, insert_uri=None, is_keypair=False):
+        """ """
+        self.require_state(QUIESCENT)
+        self.reset()
+        self.ctx['REQUEST_URI'] = request_uri
+        self.ctx['INSERT_URI'] = insert_uri
+        self.ctx['IS_KEYPAIR'] = is_keypair
+        self.ctx['REINSERT'] = 1
+        # REDFLAG: add hack code to InsertingUri to handle reinsert w/o insert uri?
+        self.transition(REQUESTING_URI_4_INSERT)
+
     # REDFLAG: UNTESTED
     def cancel(self):
         """ Start canceling the current operation. """