func interactiveSplitDiff(wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") shard := r.FormValue("shard") if keyspace == "" || shard == "" { // display the list of possible shards to chose from result := make(map[string]interface{}) shards, err := shardsWithSources(wr) if err != nil { result["Error"] = err.Error() } else { result["Shards"] = shards } executeTemplate(w, splitDiffTemplate, result) } else { // start the diff job wrk := worker.NewSplitDiffWorker(wr, *cell, keyspace, shard) if _, err := setAndStartWorker(wrk); err != nil { httpError(w, "cannot set worker: %s", err) return } http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) } }
func interactiveSplitDiff(ctx context.Context, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") shard := r.FormValue("shard") if keyspace == "" || shard == "" { // display the list of possible shards to chose from result := make(map[string]interface{}) shards, err := shardsWithSources(ctx, wr) if err != nil { result["Error"] = err.Error() } else { result["Shards"] = shards } executeTemplate(w, splitDiffTemplate, result) return } submitButtonValue := r.FormValue("submit") if submitButtonValue == "" { // display the input form result := make(map[string]interface{}) result["Keyspace"] = keyspace result["Shard"] = shard executeTemplate(w, splitDiffTemplate2, result) return } // Process input form. excludeTables := r.FormValue("excludeTables") var excludeTableArray []string if excludeTables != "" { excludeTableArray = strings.Split(excludeTables, ",") } // start the diff job wrk := worker.NewSplitDiffWorker(wr, *cell, keyspace, shard, excludeTableArray) if _, err := setAndStartWorker(wrk); err != nil { httpError(w, "cannot set worker: %s", err) return } http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) }
func interactiveVerticalSplitClone(ctx context.Context, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") if keyspace == "" { // display the list of possible keyspaces to choose from result := make(map[string]interface{}) keyspaces, err := keyspacesWithServedFrom(ctx, wr) if err != nil { result["Error"] = err.Error() } else { result["Keyspaces"] = keyspaces } executeTemplate(w, verticalSplitCloneTemplate, result) return } tables := r.FormValue("tables") if tables == "" { // display the input form result := make(map[string]interface{}) result["Keyspace"] = keyspace result["DefaultSourceReaderCount"] = fmt.Sprintf("%v", defaultSourceReaderCount) result["DefaultDestinationPackCount"] = fmt.Sprintf("%v", defaultDestinationPackCount) result["DefaultMinTableSizeForSplit"] = fmt.Sprintf("%v", defaultMinTableSizeForSplit) result["DefaultDestinationWriterCount"] = fmt.Sprintf("%v", defaultDestinationWriterCount) executeTemplate(w, verticalSplitCloneTemplate2, result) return } tableArray := strings.Split(tables, ",") // get other parameters strategy := r.FormValue("strategy") sourceReaderCountStr := r.FormValue("sourceReaderCount") sourceReaderCount, err := strconv.ParseInt(sourceReaderCountStr, 0, 64) if err != nil { httpError(w, "cannot parse sourceReaderCount: %s", err) return } destinationPackCountStr := r.FormValue("destinationPackCount") destinationPackCount, err := strconv.ParseInt(destinationPackCountStr, 0, 64) if err != nil { httpError(w, "cannot parse destinationPackCount: %s", err) return } minTableSizeForSplitStr := r.FormValue("minTableSizeForSplit") minTableSizeForSplit, err := strconv.ParseInt(minTableSizeForSplitStr, 0, 64) if err != nil { httpError(w, "cannot parse minTableSizeForSplit: %s", err) return } destinationWriterCountStr := r.FormValue("destinationWriterCount") destinationWriterCount, err := strconv.ParseInt(destinationWriterCountStr, 0, 64) if err != nil { httpError(w, "cannot parse destinationWriterCount: %s", err) return } // start the clone job wrk, err := worker.NewVerticalSplitCloneWorker(wr, *cell, keyspace, "0", tableArray, strategy, int(sourceReaderCount), int(destinationPackCount), uint64(minTableSizeForSplit), int(destinationWriterCount)) if err != nil { httpError(w, "cannot create worker: %v", err) } if _, err := setAndStartWorker(wrk); err != nil { httpError(w, "cannot set worker: %s", err) return } http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) }
// InitStatusHandling installs webserver handlers for global actions like /status, /reset and /cancel. func (wi *Instance) InitStatusHandling() { // code to serve /status workerTemplate := mustParseTemplate("worker", workerStatusHTML) http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { acl.SendError(w, err) return } wi.currentWorkerMutex.Lock() wrk := wi.currentWorker logger := wi.currentMemoryLogger ctx := wi.currentContext err := wi.lastRunError wi.currentWorkerMutex.Unlock() data := make(map[string]interface{}) if wrk != nil { status := template.HTML("Current worker:<br>\n") + wrk.StatusAsHTML() if ctx == nil { data["Done"] = true if err != nil { status += template.HTML(fmt.Sprintf("<br>\nEnded with an error: %v<br>\n", err)) } } data["Status"] = status if logger != nil { data["Logs"] = template.HTML(strings.Replace(logger.String(), "\n", "</br>\n", -1)) } else { data["Logs"] = template.HTML("See console for logs</br>\n") } } executeTemplate(w, workerTemplate, data) }) // add the section in status that does auto-refresh of status div servenv.AddStatusPart("Worker Status", workerStatusPartHTML, func() interface{} { return nil }) // reset handler http.HandleFunc("/reset", func(w http.ResponseWriter, r *http.Request) { if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { acl.SendError(w, err) return } if err := wi.Reset(); err != nil { httpError(w, err.Error(), nil) } else { // No worker currently running, we go to the menu. http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } }) // cancel handler http.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) { if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { acl.SendError(w, err) return } wi.currentWorkerMutex.Lock() // no worker, or not running, we go to the menu if wi.currentWorker == nil || wi.currentCancelFunc == nil { wi.currentWorkerMutex.Unlock() http.Redirect(w, r, "/", http.StatusTemporaryRedirect) return } // otherwise, cancel the running worker and go back to the status page cancel := wi.currentCancelFunc wi.currentWorkerMutex.Unlock() cancel() http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) }) }
// InitInteractiveMode installs webserver handlers for each known command. func (wi *Instance) InitInteractiveMode() { indexTemplate := mustParseTemplate("index", indexHTML) subIndexTemplate := mustParseTemplate("subIndex", subIndexHTML) // toplevel menu http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { acl.SendError(w, err) return } executeTemplate(w, indexTemplate, commands) }) // command group menus for _, cg := range commands { // keep a local copy of the Command pointer for the // closure. pcg := cg http.HandleFunc("/"+cg.Name, func(w http.ResponseWriter, r *http.Request) { if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { acl.SendError(w, err) return } executeTemplate(w, subIndexTemplate, pcg) }) for _, c := range cg.Commands { // keep a local copy of the Command pointer for the closure. pc := c http.HandleFunc("/"+cg.Name+"/"+c.Name, func(w http.ResponseWriter, r *http.Request) { if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { acl.SendError(w, err) return } wrk, template, data, err := pc.Interactive(wi.backgroundContext, wi, wi.wr, w, r) if err != nil { httpError(w, "%s", err) } else if template != nil && data != nil { executeTemplate(w, template, data) return } if wrk == nil { httpError(w, "Internal server error. Command: %s did not return correct response.", c.Name) return } if _, err := wi.setAndStartWorker(wrk, wi.wr); err != nil { httpError(w, "Could not set %s worker: %s", c.Name, err) return } http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) }) } } log.Infof("Interactive mode ready") }
func interactiveSplitClone(wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") shard := r.FormValue("shard") if keyspace == "" || shard == "" { // display the list of possible splits to choose from // (just find all the overlapping guys) result := make(map[string]interface{}) choices, err := keyspacesWithOverlappingShards(wr) if err != nil { result["Error"] = err.Error() } else { result["Choices"] = choices } executeTemplate(w, splitCloneTemplate, result) return } sourceReaderCountStr := r.FormValue("sourceReaderCount") if sourceReaderCountStr == "" { // display the input form result := make(map[string]interface{}) result["Keyspace"] = keyspace result["Shard"] = shard result["DefaultSourceReaderCount"] = fmt.Sprintf("%v", defaultSourceReaderCount) result["DefaultMinTableSizeForSplit"] = fmt.Sprintf("%v", defaultMinTableSizeForSplit) result["DefaultDestinationWriterCount"] = fmt.Sprintf("%v", defaultDestinationWriterCount) executeTemplate(w, splitCloneTemplate2, result) return } // get other parameters excludeTables := r.FormValue("excludeTables") excludeTableArray := strings.Split(excludeTables, ",") strategy := r.FormValue("strategy") sourceReaderCount, err := strconv.ParseInt(sourceReaderCountStr, 0, 64) if err != nil { httpError(w, "cannot parse sourceReaderCount: %s", err) return } minTableSizeForSplitStr := r.FormValue("minTableSizeForSplit") minTableSizeForSplit, err := strconv.ParseInt(minTableSizeForSplitStr, 0, 64) if err != nil { httpError(w, "cannot parse minTableSizeForSplit: %s", err) return } destinationWriterCountStr := r.FormValue("destinationWriterCount") destinationWriterCount, err := strconv.ParseInt(destinationWriterCountStr, 0, 64) if err != nil { httpError(w, "cannot parse destinationWriterCount: %s", err) return } // start the clone job wrk := worker.NewSplitCloneWorker(wr, *cell, keyspace, shard, excludeTableArray, strategy, int(sourceReaderCount), uint64(minTableSizeForSplit), int(destinationWriterCount)) if _, err := setAndStartWorker(wrk); err != nil { httpError(w, "cannot set worker: %s", err) return } http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) }