Example #1
0
func zkResolveWildcards(wr *wrangler.Wrangler, args []string) ([]string, error) {
	zkts, ok := wr.TopoServer().(*zktopo.Server)
	if !ok {
		return args, nil
	}
	return zk.ResolveWildcards(zkts.GetZConn(), args)
}
Example #2
0
// HandlePath is part of the Explorer interface
func (ex ZkExplorer) HandlePath(zkPath string, r *http.Request) *explorer.Result {
	result := &explorer.Result{}

	if zkPath == "/" {
		cells, err := zk.ResolveWildcards(ex.zconn, []string{"/zk/*"})
		if err != nil {
			result.Error = err.Error()
			return result
		}
		for i, cell := range cells {
			cells[i] = cell[4:] // cut off "/zk/"
		}
		result.Children = cells
		sort.Strings(result.Children)
		return result
	}

	zkPath = "/zk" + zkPath
	data, _, err := ex.zconn.Get(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	result.Data = data
	children, _, err := ex.zconn.Children(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	result.Children = children
	sort.Strings(result.Children)
	return result
}
Example #3
0
func cmdChmod(subFlags *flag.FlagSet, args []string) error {
	subFlags.Parse(args)
	if subFlags.NArg() < 2 {
		return fmt.Errorf("chmod: no permission specified")
	}
	mode := subFlags.Arg(0)
	if mode[0] != 'n' {
		return fmt.Errorf("chmod: invalid mode")
	}

	addPerms := false
	if mode[1] == '+' {
		addPerms = true
	} else if mode[1] != '-' {
		return fmt.Errorf("chmod: invalid mode")
	}

	var permMask uint32
	for _, c := range mode[2:] {
		permMask |= charPermMap[string(c)]
	}

	resolved, err := zk.ResolveWildcards(zconn, subFlags.Args()[1:])
	if err != nil {
		return fmt.Errorf("chmod: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// the wildcards didn't result in anything, we're done
		return nil
	}

	hasError := false
	for _, arg := range resolved {
		zkPath := fixZkPath(arg)
		aclv, _, err := zconn.ACL(zkPath)
		if err != nil {
			hasError = true
			log.Warningf("chmod: cannot set access %v: %v", zkPath, err)
			continue
		}
		if addPerms {
			aclv[0].Perms |= permMask
		} else {
			aclv[0].Perms &= ^permMask
		}
		err = zconn.SetACL(zkPath, aclv, -1)
		if err != nil {
			hasError = true
			log.Warningf("chmod: cannot set access %v: %v", zkPath, err)
			continue
		}
	}
	if hasError {
		return fmt.Errorf("chmod: some paths had errors")
	}
	return nil
}
Example #4
0
func cmdChmod(args []string) {
	if len(args) < 2 {
		log.Fatal("chmod: no permission specified")
	}
	mode := args[0]
	if mode[0] != 'n' {
		log.Fatal("chmod: invalid mode")
	}

	addPerms := false
	if mode[1] == '+' {
		addPerms = true
	} else if mode[1] != '-' {
		log.Fatal("chmod: invalid mode")
	}

	var permMask uint32
	for _, c := range mode[2:] {
		permMask |= charPermMap[string(c)]
	}

	args, err := zk.ResolveWildcards(zconn, args[1:])
	if err != nil {
		log.Fatalf("chmod: invalid wildcards: %v", err)
	}
	if len(args) == 0 {
		// the wildcards didn't result in anything, we're done
		return
	}

	hasError := false
	for _, arg := range args {
		zkPath := fixZkPath(arg)
		aclv, _, err := zconn.ACL(zkPath)
		if err != nil {
			hasError = true
			log.Printf("chmod: cannot set access %v: %v", zkPath, err)
			continue
		}
		if addPerms {
			aclv[0].Perms |= permMask
		} else {
			aclv[0].Perms &= ^permMask
		}
		err = zconn.SetACL(zkPath, aclv, -1)
		if err != nil {
			hasError = true
			log.Printf("chmod: cannot set access %v: %v", zkPath, err)
			continue
		}
	}
	if hasError {
		os.Exit(1)
	}
}
Example #5
0
func cmdRm(subFlags *flag.FlagSet, args []string) error {
	var (
		force             = subFlags.Bool("f", false, "no warning on nonexistent node")
		recursiveDelete   = subFlags.Bool("r", false, "recursive delete")
		forceAndRecursive = subFlags.Bool("rf", false, "shorthand for -r -f")
	)
	subFlags.Parse(args)
	*force = *force || *forceAndRecursive
	*recursiveDelete = *recursiveDelete || *forceAndRecursive

	if subFlags.NArg() == 0 {
		return fmt.Errorf("rm: no path specified")
	}

	if *recursiveDelete {
		for _, arg := range subFlags.Args() {
			zkPath := fixZkPath(arg)
			if strings.Count(zkPath, "/") < 4 {
				return fmt.Errorf("rm: overly general path: %v", zkPath)
			}
		}
	}

	resolved, err := zk.ResolveWildcards(zconn, subFlags.Args())
	if err != nil {
		return fmt.Errorf("rm: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// the wildcards didn't result in anything, we're done
		return nil
	}

	hasError := false
	for _, arg := range resolved {
		zkPath := fixZkPath(arg)
		var err error
		if *recursiveDelete {
			err = zk.DeleteRecursive(zconn, zkPath, -1)
		} else {
			err = zconn.Delete(zkPath, -1)
		}
		if err != nil && (!*force || !zookeeper.IsError(err, zookeeper.ZNONODE)) {
			hasError = true
			log.Warningf("rm: cannot delete %v: %v", zkPath, err)
		}
	}
	if hasError {
		// to be consistent with the command line 'rm -f', return
		// 0 if using 'zk rm -f' and the file doesn't exist.
		return fmt.Errorf("rm: some paths had errors")
	}
	return nil
}
Example #6
0
func cmdStat(subFlags *flag.FlagSet, args []string) error {
	var (
		force = subFlags.Bool("f", false, "no warning on nonexistent node")
	)
	subFlags.Parse(args)

	if subFlags.NArg() == 0 {
		return fmt.Errorf("stat: no path specified")
	}

	resolved, err := zk.ResolveWildcards(zconn, subFlags.Args())
	if err != nil {
		return fmt.Errorf("stat: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// the wildcards didn't result in anything, we're done
		return nil
	}

	hasError := false
	for _, arg := range resolved {
		zkPath := fixZkPath(arg)
		acls, stat, err := zconn.ACL(zkPath)
		if stat == nil {
			err = fmt.Errorf("no such node")
		}
		if err != nil {
			hasError = true
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				log.Warningf("stat: cannot access %v: %v", zkPath, err)
			}
			continue
		}
		fmt.Printf("Path: %s\n", zkPath)
		fmt.Printf("Created: %s\n", stat.CTime().Format(timeFmtMicro))
		fmt.Printf("Modified: %s\n", stat.MTime().Format(timeFmtMicro))
		fmt.Printf("Size: %v\n", stat.DataLength())
		fmt.Printf("Children: %v\n", stat.NumChildren())
		fmt.Printf("Version: %v\n", stat.Version())
		fmt.Printf("Ephemeral: %v\n", stat.EphemeralOwner())
		fmt.Printf("ACL:\n")
		for _, acl := range acls {
			fmt.Printf(" %v:%v %v\n", acl.Scheme, acl.Id, fmtAcl(acl))
		}
	}
	if hasError {
		return fmt.Errorf("stat: some paths had errors")
	}
	return nil
}
Example #7
0
func (ex ZkExplorer) HandlePath(actionRepo *ActionRepository, zkPath string, r *http.Request) interface{} {
	result := NewZkResult(zkPath)

	if zkPath == "/zk" {
		cells, err := zk.ResolveWildcards(ex.zconn, []string{"/zk/*"})
		if err != nil {
			result.Error = err.Error()
			return result
		}
		for i, cell := range cells {
			cells[i] = cell[4:] // cut off "/zk/"
		}
		result.Children = cells
		sort.Strings(result.Children)
		return result
	}

	data, _, err := ex.zconn.Get(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	if m, _ := path.Match("/zk/global/vt/keyspaces/*", zkPath); m {
		keyspace := path.Base(zkPath)
		actionRepo.PopulateKeyspaceActions(result.Actions, keyspace)
	} else if m, _ := path.Match("/zk/global/vt/keyspaces/*/shards/*", zkPath); m {
		zkPathParts := strings.Split(zkPath, "/")
		keyspace := zkPathParts[5]
		shard := zkPathParts[7]
		actionRepo.PopulateShardActions(result.Actions, keyspace, shard)
	} else if m, _ := path.Match("/zk/*/vt/tablets/*", result.Path); m {
		zkPathParts := strings.Split(result.Path, "/")
		alias := zkPathParts[2] + "-" + zkPathParts[5]
		actionRepo.PopulateTabletActions(result.Actions, alias, r)
		ex.addTabletLinks(data, result)
	}
	result.Data = data
	children, _, err := ex.zconn.Children(zkPath)
	if err != nil {
		result.Error = err.Error()
		return result
	}
	result.Children = children
	sort.Strings(result.Children)
	return result
}
Example #8
0
func cmdRm(args []string) {
	if len(args) == 0 {
		log.Fatal("rm: no path specified")
	}

	if *recursiveDelete {
		for _, arg := range args {
			zkPath := fixZkPath(arg)
			if strings.Count(zkPath, "/") < 4 {
				log.Fatalf("rm: overly general path: %v", zkPath)
			}
		}
	}

	args, err := zk.ResolveWildcards(zconn, args)
	if err != nil {
		log.Fatalf("rm: invalid wildcards: %v", err)
	}
	if len(args) == 0 {
		// the wildcards didn't result in anything, we're done
		return
	}

	hasError := false
	for _, arg := range args {
		zkPath := fixZkPath(arg)
		var err error
		if *recursiveDelete {
			err = zk.DeleteRecursive(zconn, zkPath, -1)
		} else {
			err = zconn.Delete(zkPath, -1)
		}
		if err != nil {
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				hasError = true
				log.Printf("rm: cannot delete %v: %v", zkPath, err)
			}
		}
	}
	if hasError {
		// to be consistent with the command line 'rm -f', return
		// 0 if using 'zk rm -f' and the file doesn't exist.
		os.Exit(1)
	}
}
Example #9
0
func cmdStat(args []string) {
	if len(args) == 0 {
		log.Fatal("stat: no path specified")
	}
	args, err := zk.ResolveWildcards(zconn, args)
	if err != nil {
		log.Fatalf("stat: invalid wildcards: %v", err)
	}
	if len(args) == 0 {
		// the wildcards didn't result in anything, we're done
		return
	}

	hasError := false
	for _, arg := range args {
		zkPath := fixZkPath(arg)
		acls, stat, err := zconn.ACL(zkPath)
		if stat == nil {
			err = fmt.Errorf("no such node")
		}
		if err != nil {
			hasError = true
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				log.Printf("stat: cannot access %v: %v", zkPath, err)
			}
			continue
		}
		fmt.Printf("Path: %s\n", zkPath)
		fmt.Printf("Created: %s\n", stat.CTime().Format(timeFmtMicro))
		fmt.Printf("Modified: %s\n", stat.MTime().Format(timeFmtMicro))
		fmt.Printf("Size: %v\n", stat.DataLength())
		fmt.Printf("Children: %v\n", stat.NumChildren())
		fmt.Printf("Version: %v\n", stat.Version())
		fmt.Printf("Ephemeral: %v\n", stat.EphemeralOwner())
		fmt.Printf("ACL:\n")
		for _, acl := range acls {
			fmt.Printf(" %v:%v %v\n", acl.Scheme, acl.Id, fmtAcl(acl))
		}
	}
	if hasError {
		os.Exit(1)
	}
}
Example #10
0
func cmdCat(subFlags *flag.FlagSet, args []string) error {
	var (
		longListing = subFlags.Bool("l", false, "long listing")
		force       = subFlags.Bool("f", false, "no warning on nonexistent node")
	)
	subFlags.Parse(args)
	if subFlags.NArg() == 0 {
		return fmt.Errorf("cat: no path specified")
	}
	resolved, err := zk.ResolveWildcards(zconn, subFlags.Args())
	if err != nil {
		return fmt.Errorf("cat: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// the wildcards didn't result in anything, we're done
		return nil
	}

	hasError := false
	for _, arg := range resolved {
		zkPath := fixZkPath(arg)
		data, _, err := zconn.Get(zkPath)
		if err != nil {
			hasError = true
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				log.Warningf("cat: cannot access %v: %v", zkPath, err)
			}
		} else {
			if *longListing {
				fmt.Printf("%v:\n", zkPath)
			}
			fmt.Print(data)
			if len(data) > 0 && data[len(data)-1] != '\n' && (terminal.IsTerminal(int(os.Stdout.Fd())) || *longListing) {
				fmt.Print("\n")
			}
		}
	}
	if hasError {
		return fmt.Errorf("cat: some paths had errors")
	}
	return nil
}
Example #11
0
func cmdCat(args []string) {
	if len(args) == 0 {
		log.Fatal("cat: no path specified")
	}
	args, err := zk.ResolveWildcards(zconn, args)
	if err != nil {
		log.Fatalf("cat: invalid wildcards: %v", err)
	}
	if len(args) == 0 {
		// the wildcards didn't result in anything, we're done
		return
	}

	hasError := false
	for _, arg := range args {
		zkPath := fixZkPath(arg)
		data, _, err := zconn.Get(zkPath)
		if err != nil {
			hasError = true
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				log.Printf("cat: cannot access %v: %v", zkPath, err)
			}
		} else {
			if *longListing {
				fmt.Printf("%v:\n", zkPath)
			}
			fmt.Print(data)
			if len(data) > 0 && data[len(data)-1] != '\n' && (terminal.IsTerminal(os.Stdout.Fd()) || *longListing) {
				fmt.Print("\n")
			}
		}
	}
	if hasError {
		os.Exit(1)
	}
}
Example #12
0
func init() {
	// handles /zk paths
	http.HandleFunc("/zk/", func(w http.ResponseWriter, r *http.Request) {
		zkTopoServ := topo.GetServerByName("zookeeper")
		if zkTopoServ == nil {
			http.Error(w, "can only look at zk with zktopo.Server", http.StatusInternalServerError)
			return
		}
		zconn := zkTopoServ.(*zktopo.Server).GetZConn()

		if err := r.ParseForm(); err != nil {
			httpError(w, "cannot parse form: %s", err)
			return
		}
		zkPath := r.URL.Path[strings.Index(r.URL.Path, "/zk"):]

		if cleanPath := path.Clean(zkPath); zkPath != cleanPath && zkPath != cleanPath+"/" {
			log.Infof("redirecting to %v", cleanPath)
			http.Redirect(w, r, cleanPath, http.StatusTemporaryRedirect)
			return
		}

		if strings.HasSuffix(zkPath, "/") {
			zkPath = zkPath[:len(zkPath)-1]
		}

		result := NewZkResult(zkPath)

		if zkPath == "/zk" {
			cells, err := zk.ResolveWildcards(zconn, []string{"/zk/*"})
			if err != nil {
				httpError(w, "zk error: %v", err)
				return
			}
			for i, cell := range cells {
				cells[i] = cell[4:] // cut off "/zk/"
			}
			result.Children = cells
			sort.Strings(result.Children)
		} else {

			if data, _, err := zconn.Get(zkPath); err != nil {
				result.Error = err.Error()
			} else {

				if m, _ := path.Match("/zk/global/vt/keyspaces/*", zkPath); m {
					keyspace := path.Base(zkPath)
					actionRepo.PopulateKeyspaceActions(result.Actions, keyspace)
				} else if m, _ := path.Match("/zk/global/vt/keyspaces/*/shards/*", zkPath); m {
					zkPathParts := strings.Split(zkPath, "/")
					keyspace := zkPathParts[5]
					shard := zkPathParts[7]
					actionRepo.PopulateShardActions(result.Actions, keyspace, shard)
				} else if m, _ := path.Match("/zk/*/vt/tablets/*", result.Path); m {
					zkPathParts := strings.Split(result.Path, "/")
					alias := zkPathParts[2] + "-" + zkPathParts[5]
					actionRepo.PopulateTabletActions(result.Actions, alias)
				}
				result.Data = data
				if children, _, err := zconn.Children(zkPath); err != nil {
					result.Error = err.Error()
				} else {
					result.Children = children
					sort.Strings(result.Children)
				}
			}
		}
		templateLoader.ServeTemplate("zk.html", result, w, r)
	})

	// adds links for keyspaces and shards
	funcMap["keyspace"] = func(keyspace string) template.HTML {
		return template.HTML("<a href=\"/zk/global/vt/keyspaces/" + keyspace + "\">" + keyspace + "</a>")
	}
	funcMap["shard"] = func(keyspace, shard string) template.HTML {
		return template.HTML("<a href=\"/zk/global/vt/keyspaces/" + keyspace + "/shards/" + shard + "\">" + shard + "</a>")
	}

	// add toplevel link for zookeeper
	indexContent.ToplevelLinks["Zookeeper Explorer"] = "/zk"
}
Example #13
0
func cmdLs(subFlags *flag.FlagSet, args []string) error {
	var (
		longListing      = subFlags.Bool("l", false, "long listing")
		directoryListing = subFlags.Bool("d", false, "list directory instead of contents")
		force            = subFlags.Bool("f", false, "no warning on nonexistent node")
		recursiveListing = subFlags.Bool("R", false, "recursive listing")
	)
	subFlags.Parse(args)
	if subFlags.NArg() == 0 {
		return fmt.Errorf("ls: no path specified")
	}
	// FIXME(szopa): shadowing?
	resolved, err := zk.ResolveWildcards(zconn, subFlags.Args())
	if err != nil {
		return fmt.Errorf("ls: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// the wildcards didn't result in anything, we're
		// done.
		return nil
	}

	hasError := false
	needsHeader := len(resolved) > 1 && !*directoryListing
	for _, arg := range resolved {
		zkPath := fixZkPath(arg)
		var children []string
		var err error
		isDir := true
		if *directoryListing {
			children = []string{""}
			isDir = false
		} else if *recursiveListing {
			children, err = zk.ChildrenRecursive(zconn, zkPath)
		} else {
			children, _, err = zconn.Children(zkPath)
			// Assume this is a file node if it has no children.
			if len(children) == 0 {
				children = []string{""}
				isDir = false
			}
		}
		if err != nil {
			hasError = true
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				log.Warningf("ls: cannot access %v: %v", zkPath, err)
			}
		}

		// Show the full path when it helps.
		showFullPath := false
		if *recursiveListing {
			showFullPath = true
		} else if *longListing && (*directoryListing || !isDir) {
			showFullPath = true
		}
		if needsHeader {
			fmt.Printf("%v:\n", zkPath)
		}
		if len(children) > 0 {
			if *longListing && isDir {
				fmt.Printf("total: %v\n", len(children))
			}
			sort.Strings(children)
			stats := make([]zk.Stat, len(children))
			wg := sync.WaitGroup{}
			f := func(i int) {
				localPath := path.Join(zkPath, children[i])
				stat, err := zconn.Exists(localPath)
				if err != nil {
					if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
						log.Warningf("ls: cannot access: %v: %v", localPath, err)
					}
				} else {
					stats[i] = stat
				}
				wg.Done()
			}
			for i := range children {
				wg.Add(1)
				go f(i)
			}
			wg.Wait()

			for i, child := range children {
				localPath := path.Join(zkPath, child)
				if stat := stats[i]; stat != nil {
					fmtPath(stat, localPath, showFullPath, *longListing)
				}
			}
		}
		if needsHeader {
			fmt.Println()
		}
	}
	if hasError {
		return fmt.Errorf("ls: some paths had errors")
	}
	return nil
}
Example #14
0
func cmdLs(args []string) {
	if len(args) == 0 {
		log.Fatal("ls: no path specified")
	}
	args, err := zk.ResolveWildcards(zconn, args)
	if err != nil {
		log.Fatalf("ls: invalid wildcards: %v", err)
	}
	if len(args) == 0 {
		// the wildcards didn't result in anything, we're done
		return
	}

	hasError := false
	needsHeader := len(args) > 1 && !*directoryListing
	for _, arg := range args {
		zkPath := fixZkPath(arg)
		var children []string
		var err error
		isDir := true
		if *directoryListing {
			children = []string{""}
			isDir = false
		} else if *recursiveListing {
			children, err = zk.ChildrenRecursive(zconn, zkPath)
		} else {
			children, _, err = zconn.Children(zkPath)
			// Assume this is a file node if it has no children.
			if len(children) == 0 {
				children = []string{""}
				isDir = false
			}
		}
		if err != nil {
			hasError = true
			if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
				log.Printf("ls: cannot access %v: %v", zkPath, err)
			}
		}

		// Show the full path when it helps.
		showFullPath := false
		if *recursiveListing {
			showFullPath = true
		} else if *longListing && (*directoryListing || !isDir) {
			showFullPath = true
		}
		if needsHeader {
			fmt.Printf("%v:\n", zkPath)
		}
		if len(children) > 0 {
			if *longListing && isDir {
				fmt.Printf("total: %v\n", len(children))
			}
			sort.Strings(children)
			stats := make([]zk.Stat, len(children))
			wg := sync.WaitGroup{}
			f := func(i int) {
				localPath := path.Join(zkPath, children[i])
				stat, err := zconn.Exists(localPath)
				if err != nil {
					if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) {
						log.Printf("ls: cannot access: %v: %v", localPath, err)
					}
				} else {
					stats[i] = stat
				}
				wg.Done()
			}
			for i := range children {
				wg.Add(1)
				go f(i)
			}
			wg.Wait()

			for i, child := range children {
				localPath := path.Join(zkPath, child)
				if stat := stats[i]; stat != nil {
					fmtPath(stat, localPath, showFullPath)
				}
			}
		}
		if needsHeader {
			fmt.Println()
		}
	}
	if hasError {
		os.Exit(1)
	}
}
Example #15
0
func main() {
	flag.Parse()

	templateLoader := NewTemplateLoader(*templateDir, dummyTemplate, *debug)

	ts := topo.GetServer()
	defer topo.CloseServers()

	wr := wrangler.New(ts, 30*time.Second, 30*time.Second)

	actionRepo := NewActionRepository(wr)

	const (
		keyspacePath = "/zk/global/vt/keyspaces/*"
		shardPath    = "/zk/global/vt/keyspaces/*/shards/*"
		tabletPath   = "/zk/*/vt/tablets/*"
	)

	actionRepo.Register(keyspacePath, "ValidateKeyspace",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			return "", wr.ValidateKeyspace(path.Base(zkPath), false)
		})

	actionRepo.Register(keyspacePath, "ValidateSchemaKeyspace",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			return "", wr.ValidateSchemaKeyspace(path.Base(zkPath), false)
		})

	actionRepo.Register(keyspacePath, "ValidateVersionKeyspace",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			return "", wr.ValidateVersionKeyspace(path.Base(zkPath))
		})

	actionRepo.Register(keyspacePath, "ValidatePermissionsKeyspace",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			return "", wr.ValidatePermissionsKeyspace(path.Base(zkPath))
		})

	actionRepo.Register(shardPath, "ValidateShard",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			zkPathParts := strings.Split(zkPath, "/")
			return "", wr.ValidateShard(zkPathParts[5], zkPathParts[7], false)
		})

	actionRepo.Register(shardPath, "ValidateSchemaShard",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			zkPathParts := strings.Split(zkPath, "/")
			return "", wr.ValidateSchemaShard(zkPathParts[5], zkPathParts[7], false)
		})

	actionRepo.Register(shardPath, "ValidateVersionShard",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			zkPathParts := strings.Split(zkPath, "/")
			return "", wr.ValidateVersionShard(zkPathParts[5], zkPathParts[7])
		})

	actionRepo.Register(shardPath, "ValidatePermissionsShard",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			zkPathParts := strings.Split(zkPath, "/")
			return "", wr.ValidatePermissionsShard(zkPathParts[5], zkPathParts[7])
		})

	actionRepo.Register(tabletPath, "RpcPing",
		func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) {
			zkPathParts := strings.Split(zkPath, "/")
			alias, err := topo.ParseTabletAliasString(zkPathParts[2] + "-" + zkPathParts[5])
			if err != nil {
				return "", err
			}
			return "", wr.ActionInitiator().RpcPing(alias, 10*time.Second)
		})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		tmpl, err := templateLoader.Lookup("index.html")
		if err != nil {
			httpError(w, "error in template loader: %v", err)
			return
		}
		if err := tmpl.Execute(w, nil); err != nil {
			httpError(w, "error executing template", err)
		}

	})
	http.HandleFunc("/actions", func(w http.ResponseWriter, r *http.Request) {
		if err := r.ParseForm(); err != nil {
			httpError(w, "cannot parse form: %s", err)
			return
		}
		action := r.FormValue("action")
		if action == "" {
			http.Error(w, "no action provided", http.StatusBadRequest)
			return
		}

		zkPath := r.FormValue("zkpath")
		if zkPath == "" {
			http.Error(w, "no zookeeper path provided", http.StatusBadRequest)
			return
		}
		result := actionRepo.Apply(action, zkPath, r)

		templateLoader.ServeTemplate("action.html", result, w, r)
	})
	http.HandleFunc("/dbtopo", func(w http.ResponseWriter, r *http.Request) {
		if err := r.ParseForm(); err != nil {
			httpError(w, "cannot parse form: %s", err)
			return
		}
		result := DbTopologyResult{}
		topology, err := wr.DbTopology()
		if err != nil {
			result.Error = err.Error()
		} else {
			result.Topology = topology
		}
		templateLoader.ServeTemplate("dbtopo.html", result, w, r)
	})
	http.HandleFunc("/zk/", func(w http.ResponseWriter, r *http.Request) {
		zkTopoServ := topo.GetServerByName("zookeeper")
		if zkTopoServ == nil {
			http.Error(w, "can only look at zk with zktopo.Server", http.StatusInternalServerError)
			return
		}
		zconn := zkTopoServ.(*zktopo.Server).GetZConn()

		if err := r.ParseForm(); err != nil {
			httpError(w, "cannot parse form: %s", err)
			return
		}
		zkPath := r.URL.Path[strings.Index(r.URL.Path, "/zk"):]

		if cleanPath := path.Clean(zkPath); zkPath != cleanPath && zkPath != cleanPath+"/" {
			relog.Info("redirecting to %v", cleanPath)
			http.Redirect(w, r, cleanPath, http.StatusTemporaryRedirect)
			return
		}

		if strings.HasSuffix(zkPath, "/") {
			zkPath = zkPath[:len(zkPath)-1]
		}

		result := NewZkResult(zkPath)

		if zkPath == "/zk" {
			cells, err := zk.ResolveWildcards(zconn, []string{"/zk/*"})
			if err != nil {
				httpError(w, "zk error: %v", err)
				return
			}
			for i, cell := range cells {
				cells[i] = cell[4:] // cut off "/zk/"
			}
			result.Children = cells
		} else {

			if data, _, err := zconn.Get(zkPath); err != nil {
				result.Error = err.Error()
			} else {
				actionRepo.PopulateAvailableActions(result)
				result.Data = data
				if children, _, err := zconn.Children(zkPath); err != nil {
					result.Error = err.Error()
				} else {
					result.Children = children
				}
			}
		}
		templateLoader.ServeTemplate("zk.html", result, w, r)
	})
	relog.Fatal("%s", http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
}