Moving a few status functions to this source tree.

They allow the status page of vttablet to be better.
Also testing this code in vtctld_test, so we make sure the
templates work. And manual inspection works very well too.
This commit is contained in:
Alain Jobart 2014-05-14 09:41:49 -07:00
Родитель f7df4e7e97
Коммит 905eb39851
4 изменённых файлов: 198 добавлений и 16 удалений

Просмотреть файл

@ -5,11 +5,20 @@ import (
"github.com/youtube/vitess/go/vt/health"
"github.com/youtube/vitess/go/vt/servenv"
_ "github.com/youtube/vitess/go/vt/status"
"github.com/youtube/vitess/go/vt/tabletmanager"
"github.com/youtube/vitess/go/vt/tabletserver"
)
var (
tabletTemplate = `
Alias: {{github_com_youtube_vitess_vtctld_tablet .Alias.String}}<br>
Keyspace: {{github_com_youtube_vitess_vtctld_keyspace .Keyspace}} Shard: {{github_com_youtube_vitess_vtctld_shard .Keyspace .Shard}}<br>
Serving graph: {{github_com_youtube_vitess_vtctld_srv_keyspace .Alias.Cell .Keyspace}} {{github_com_youtube_vitess_vtctld_srv_shard .Alias.Cell .Keyspace .Shard}} {{github_com_youtube_vitess_vtctld_srv_type .Alias.Cell .Keyspace .Shard .Type}}<br>
Replication graph: {{github_com_youtube_vitess_vtctld_replication .Alias.Cell .Keyspace .Shard}}<br>
State: {{.State}}<br>
`
healthTemplate = `
<style>
table {
@ -85,6 +94,9 @@ func healthHTMLName() template.HTML {
func init() {
servenv.OnRun(func() {
servenv.AddStatusPart("Tablet", tabletTemplate, func() interface{} {
return agent.Tablet()
})
if agent.IsRunningHealthCheck() {
servenv.AddStatusFuncs(template.FuncMap{
"github_com_youtube_vitess_health_html_name": healthHTMLName,

144
go/vt/status/status.go Normal file
Просмотреть файл

@ -0,0 +1,144 @@
// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package status defines a few useful functions for our binaries,
// mainly to link the status page with a vtctld instance.
package status
import (
"flag"
"fmt"
"html/template"
"net/url"
"strings"
"github.com/youtube/vitess/go/vt/servenv"
"github.com/youtube/vitess/go/vt/topo"
)
var (
vtctldAddr = flag.String("vtctld_addr", "", "address of a vtctld instance")
vtctldTopoExplorer = flag.String("vtctld_topo_explorer", "zk", "topo explorer to be used in status links")
)
// MakeVtctldRedirect returns an absolute vtctld url that will
// redirect to the page for the topology object specified in q.
func MakeVtctldRedirect(text string, q map[string]string) template.HTML {
query := url.Values{}
for k, v := range q {
query.Set(k, v)
}
url := "/explorers/redirect" + "?" + query.Encode()
return VtctldLink(text, url)
}
// VtctldLink returns the HTML to display a link to the fully
// qualified vtctld url whose path is given as parameter.
// If no vtctld_addr flag was passed in, we just return the text with no link.
func VtctldLink(text, urlPath string) template.HTML {
if *vtctldAddr == "" {
return template.HTML(text)
}
var fullURL string
if strings.HasSuffix(*vtctldAddr, "/") {
fullURL = *vtctldAddr + urlPath
} else {
fullURL = *vtctldAddr + "/" + urlPath
}
return template.HTML(fmt.Sprintf(`<a href="%v">%v</a>`, fullURL, text))
}
// VtctldKeyspace returns the keyspace name, possibly linked to the
// keyspace page in vtctld.
func VtctldKeyspace(keyspace string) template.HTML {
return MakeVtctldRedirect(keyspace,
map[string]string{
"type": "keyspace",
"explorer": *vtctldTopoExplorer,
"keyspace": keyspace,
})
}
// VtctldShard returns the shard name, possibly linked to the shard
// page in vtctld.
func VtctldShard(keyspace, shard string) template.HTML {
return MakeVtctldRedirect(shard, map[string]string{
"type": "shard",
"explorer": *vtctldTopoExplorer,
"keyspace": keyspace,
"shard": shard,
})
}
// VtctldSrvKeyspace returns the keyspace name, possibly linked to the
// SrvKeyspace page in vtctld.
func VtctldSrvKeyspace(cell, keyspace string) template.HTML {
return MakeVtctldRedirect(keyspace, map[string]string{
"type": "srv_keyspace",
"explorer": *vtctldTopoExplorer,
"cell": cell,
"keyspace": keyspace,
})
}
// VtctldSrvShard returns the shard name, possibly linked to the
// SrvShard page in vtctld.
func VtctldSrvShard(cell, keyspace, shard string) template.HTML {
return MakeVtctldRedirect(shard, map[string]string{
"type": "srv_shard",
"explorer": *vtctldTopoExplorer,
"cell": cell,
"keyspace": keyspace,
"shard": shard,
})
}
// VtctldSrvType returns the tablet type, possibly linked to the
// EndPoints page in vtctld.
func VtctldSrvType(cell, keyspace, shard string, tabletType topo.TabletType) template.HTML {
return MakeVtctldRedirect(string(tabletType), map[string]string{
"type": "srv_type",
"explorer": *vtctldTopoExplorer,
"cell": cell,
"keyspace": keyspace,
"shard": shard,
"tablet_type": string(tabletType),
})
}
// VtctldReplication returns 'cell/keyspace/shard', possibly linked to the
// ShardReplication page in vtctld.
func VtctldReplication(cell, keyspace, shard string) template.HTML {
return MakeVtctldRedirect(fmt.Sprintf("%v/%v/%v", cell, keyspace, shard),
map[string]string{
"type": "replication",
"explorer": *vtctldTopoExplorer,
"keyspace": keyspace,
"shard": shard,
"cell": cell,
})
}
// VtctldTablet returns the tablet alias, possibly linked to the
// Tablet page in vtctld.
func VtctldTablet(aliasName string) template.HTML {
return MakeVtctldRedirect(aliasName, map[string]string{
"type": "tablet",
"explorer": *vtctldTopoExplorer,
"alias": aliasName,
})
}
func init() {
servenv.AddStatusFuncs(template.FuncMap{
"github_com_youtube_vitess_vtctld_keyspace": VtctldKeyspace,
"github_com_youtube_vitess_vtctld_shard": VtctldShard,
"github_com_youtube_vitess_vtctld_srv_keyspace": VtctldSrvKeyspace,
"github_com_youtube_vitess_vtctld_srv_shard": VtctldSrvShard,
"github_com_youtube_vitess_vtctld_srv_type": VtctldSrvType,
"github_com_youtube_vitess_vtctld_replication": VtctldReplication,
"github_com_youtube_vitess_vtctld_tablet": VtctldTablet,
})
}

Просмотреть файл

@ -287,7 +287,8 @@ class Tablet(object):
wait_for_state="SERVING", customrules=None,
schema_override=None, cert=None, key=None, ca_cert=None,
repl_extra_flags={}, sensitive_mode=False,
target_tablet_type=None, lameduck_period=None):
target_tablet_type=None, lameduck_period=None,
extra_args=None):
"""
Starts a vttablet process, and returns it.
The process is also saved in self.proc, so it's easy to kill as well.
@ -335,6 +336,8 @@ class Tablet(object):
'-allowed_replication_lag', '30'])
if lameduck_period:
args.extend(['-lameduck-period', lameduck_period])
if extra_args:
args.extend(extra_args)
stderr_fd = open(os.path.join(self.tablet_dir, "vttablet.stderr"), "w")
# increment count only the first time

Просмотреть файл

@ -17,11 +17,12 @@ shard_0_spare = tablet.Tablet()
# range 80 - ""
shard_1_master = tablet.Tablet()
shard_1_replica = tablet.Tablet()
# not assigned
idle = tablet.Tablet()
scrap = tablet.Tablet()
assigned = [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica]
tablets = assigned + [idle, scrap, shard_0_spare]
# all tablets
tablets = [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica,
idle, scrap, shard_0_spare]
class VtctldError(Exception): pass
@ -58,9 +59,13 @@ class Vtctld(object):
self.proc = utils.run_bg(args, stderr=stderr_fd)
return self.proc
def process_args(self):
return ['-vtctld_addr', 'http://localhost:%u/' % self.port]
vtctld = Vtctld()
def setUpModule():
try:
environment.topo_server_setup()
@ -73,6 +78,7 @@ def setUpModule():
tearDownModule()
raise
def tearDownModule():
if utils.options.skip_teardown:
return
@ -95,7 +101,7 @@ class TestVtctld(unittest.TestCase):
utils.run_vtctl('CreateKeyspace test_keyspace')
shard_0_master.init_tablet( 'master', 'test_keyspace', '-80')
shard_0_replica.init_tablet('replica', 'test_keyspace', '-80')
shard_0_replica.init_tablet('spare', 'test_keyspace', '-80')
shard_0_spare.init_tablet('spare', 'test_keyspace', '-80')
shard_1_master.init_tablet( 'master', 'test_keyspace', '80-')
shard_1_replica.init_tablet('replica', 'test_keyspace', '80-')
@ -104,21 +110,28 @@ class TestVtctld(unittest.TestCase):
utils.run_vtctl('RebuildKeyspaceGraph test_keyspace', auto_log=True)
for t in assigned:
for t in [shard_0_master, shard_1_master, shard_1_replica]:
t.create_db('vt_test_keyspace')
t.start_vttablet()
t.start_vttablet(extra_args=vtctld.process_args())
shard_0_replica.create_db('vt_test_keyspace')
shard_0_replica.start_vttablet(extra_args=vtctld.process_args(),
target_tablet_type='replica',
wait_for_state='NOT_SERVING')
for t in scrap, idle, shard_0_spare:
t.start_vttablet(wait_for_state='NOT_SERVING')
t.start_vttablet(wait_for_state='NOT_SERVING',
extra_args=vtctld.process_args())
scrap.scrap()
for t in [shard_0_master, shard_0_replica, shard_0_spare,
shard_1_master, shard_1_replica, idle, scrap]:
t.reset_replication()
utils.run_vtctl('ReparentShard -force test_keyspace/-80 ' + shard_0_master.tablet_alias, auto_log=True)
utils.run_vtctl('ReparentShard -force test_keyspace/80- ' + shard_1_master.tablet_alias, auto_log=True)
utils.run_vtctl('ReparentShard -force test_keyspace/-80 ' +
shard_0_master.tablet_alias, auto_log=True)
utils.run_vtctl('ReparentShard -force test_keyspace/80- ' +
shard_1_master.tablet_alias, auto_log=True)
shard_0_replica.wait_for_vttablet_state('SERVING')
# run checks now before we start the tablets
utils.validate_topology()
@ -130,7 +143,8 @@ class TestVtctld(unittest.TestCase):
def test_assigned(self):
logging.debug("test_assigned: %s", str(self.data))
self.assertItemsEqual(self.data["Assigned"].keys(), ["test_keyspace"])
self.assertItemsEqual(self.data["Assigned"]["test_keyspace"].keys(), ["-80", "80-"])
self.assertItemsEqual(self.data["Assigned"]["test_keyspace"].keys(),
["-80", "80-"])
def test_not_assigned(self):
self.assertEqual(len(self.data["Idle"]), 1)
@ -159,12 +173,21 @@ class TestVtctld(unittest.TestCase):
self.assertEqual(urllib2.urlopen(base + '/explorers/redirect?type=replication&explorer=zk&keyspace=test_keyspace&shard=-80&cell=test_nj').geturl(),
base + '/zk/test_nj/vt/replication/test_keyspace/-80')
def test_serving_graph(self):
self.assertItemsEqual(self.serving_data.keys(), ["test_keyspace"])
self.assertItemsEqual(self.serving_data["test_keyspace"].keys(), ["-80", "80-"])
self.assertItemsEqual(self.serving_data["test_keyspace"]["-80"].keys(), ["master", "replica"])
self.assertEqual(len(self.serving_data["test_keyspace"]["-80"]["master"]), 1)
self.assertItemsEqual(self.serving_data["test_keyspace"].keys(),
["-80", "80-"])
self.assertItemsEqual(self.serving_data["test_keyspace"]["-80"].keys(),
["master", "replica"])
self.assertEqual(len(self.serving_data["test_keyspace"]["-80"]["master"]),
1)
def test_tablet_status(self):
# the vttablet that has a health check has a bit more, so using it
shard_0_replica_status = shard_0_replica.get_status()
self.assertIn('Polling health information from MySQLReplicationLag(allowedLag=30)', shard_0_replica_status)
self.assertIn('Alias: <a href="http://localhost:', shard_0_replica_status)
self.assertIn('</html>', shard_0_replica_status)
if __name__ == '__main__':
utils.main()